diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 8cef9e0644bb0..e3e94e440f694 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -37,7 +37,6 @@ features = ['override_allocator_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'] llvm_offload = ['rustc_driver_impl/llvm_offload'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 521043e3f6c1a..fb0736a90cd8c 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1,7 +1,6 @@ -use std::collections::BTreeSet; use std::fmt::{self, Write}; use std::ops::Deref; -use std::range::RangeInclusive; +use std::range::{RangeFrom, RangeInclusive, RangeToInclusive}; use std::{cmp, iter}; use rustc_hashes::Hash64; @@ -349,8 +348,8 @@ impl LayoutCalculator { variants: &IndexSlice>, is_enum: bool, is_special_no_niche: bool, - discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), - discriminants: impl Iterator, + discr_range_of_repr: impl Fn(RangeFrom, RangeToInclusive) -> (Integer, bool), + discriminants: impl Iterator, always_sized: bool, ) -> LayoutCalculatorResult { let (present_first, present_second) = { @@ -582,8 +581,8 @@ impl LayoutCalculator { &self, repr: &ReprOptions, variants: &IndexSlice>, - discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), - discriminants: impl Iterator, + discr_range_of_repr: impl Fn(RangeFrom, RangeToInclusive) -> (Integer, bool), + discriminants: impl Iterator, ) -> LayoutCalculatorResult { let dl = self.cx.data_layout(); // bail if the enum has an incoherent repr that cannot be computed @@ -755,63 +754,36 @@ impl LayoutCalculator { let niche_filling_layout = calculate_niche_filling_layout(); let discr_type = repr.discr_type(); - let discr_int = Integer::from_attr(dl, discr_type); - // Because we can only represent one range of valid values, we'll look for the - // largest range of invalid values and pick everything else as the range of valid - // values. + let discr_size = Integer::from_attr(dl, discr_type).size(); - // First we need to sort the possible discriminant values so that we can look for the largest gap: - let valid_discriminants: BTreeSet = discriminants + let necessary_discriminants: Vec = discriminants .filter(|&(i, _)| repr.c() || variants[i].iter().all(|f| !f.is_uninhabited())) - .map(|(_, val)| { - if discr_type.is_signed() { - // sign extend the raw representation to be an i128 - // FIXME: do this at the discriminant iterator creation sites - discr_int.size().sign_extend(val as u128) - } else { - val - } - }) + .map(|(_, val)| val) .collect(); - trace!(?valid_discriminants); - let discriminants = valid_discriminants.iter().copied(); - //let next_discriminants = discriminants.clone().cycle().skip(1); - let next_discriminants = - discriminants.clone().chain(valid_discriminants.first().copied()).skip(1); - // Iterate over pairs of each discriminant together with the next one. - // Since they were sorted, we can now compute the niche sizes and pick the largest. - let discriminants = discriminants.zip(next_discriminants); - let largest_niche = discriminants.max_by_key(|&(start, end)| { - trace!(?start, ?end); - // If this is a wraparound range, the niche size is `MAX - abs(diff)`, as the diff between - // the two end points is actually the size of the valid discriminants. - let dist = if start > end { - // Overflow can happen for 128 bit discriminants if `end` is negative. - // But in that case casting to `u128` still gets us the right value, - // as the distance must be positive if the lhs of the subtraction is larger than the rhs. - let dist = start.wrapping_sub(end); - if discr_type.is_signed() { - discr_int.signed_max().wrapping_sub(dist) as u128 - } else { - discr_int.size().unsigned_int_max() - dist as u128 - } - } else { - // Overflow can happen for 128 bit discriminants if `start` is negative. - // But in that case casting to `u128` still gets us the right value, - // as the distance must be positive if the lhs of the subtraction is larger than the rhs. - end.wrapping_sub(start) as u128 - }; - trace!(?dist); - dist - }); - trace!(?largest_niche); - - // `max` is the last valid discriminant before the largest niche - // `min` is the first valid discriminant after the largest niche - let (max, min) = largest_niche + + // When picking the integer to use, we respect how the discriminants were written + // in the original rust code, rather than looking only at the bit pattern. + let (min_negative, max_positive): (i128, u128) = if discr_type.is_signed() { + necessary_discriminants.iter().copied().map(|val| discr_size.sign_extend(val)).fold( + (0_i128, 0_u128), + |(min, max), val| { + if let Ok(val) = u128::try_from(val) { + (min, max.max(val)) + } else { + (min.min(val), max) + } + }, + ) + } else { // We might have no inhabited variants, so pretend there's at least one. - .unwrap_or((0, 0)); - let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::discr_range_of_repr(tcx, ty, &repr, min, max); + (0, necessary_discriminants.iter().copied().max().unwrap_or(0)) + }; + trace!(?min_negative, ?max_positive); + + let (min_ity, signed) = discr_range_of_repr( + RangeFrom { start: min_negative }, + RangeToInclusive { last: max_positive }, + ); //Integer::discr_range_of_repr(tcx, ty, &repr, min, max); let mut align = dl.aggregate_align; let mut max_repr_align = repr.align; @@ -929,13 +901,16 @@ impl LayoutCalculator { } } - let tag_mask = ity.size().unsigned_int_max(); + let tag_valid_range = { + let tag_size = ity.size(); + let tags = necessary_discriminants.into_iter().map(|d| tag_size.truncate(d)); + WrappingRange::smallest_range_containing(tags, tag_size) + // We might have no inhabited variants, so pretend there's at least one. + .unwrap_or(WrappingRange { start: 0, end: 0 }) + }; let tag = Scalar::Initialized { value: Primitive::Int(ity, signed), - valid_range: WrappingRange { - start: (min as u128 & tag_mask), - end: (max as u128 & tag_mask), - }, + valid_range: tag_valid_range, }; let mut abi = BackendRepr::Memory { sized: true }; diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 679523341c7e3..d978920fb638d 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -41,7 +41,7 @@ use std::fmt; #[cfg(feature = "nightly")] use std::iter::Step; use std::num::{NonZeroUsize, ParseIntError}; -use std::ops::{Add, AddAssign, Deref, Mul, RangeFull, Sub}; +use std::ops::{Add, AddAssign, Deref, Mul, Sub}; use std::range::RangeInclusive; use std::str::FromStr; @@ -65,6 +65,7 @@ mod extern_abi; mod layout; #[cfg(test)] mod tests; +mod wrapping_range; pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind}; pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call}; @@ -74,6 +75,7 @@ pub use extern_abi::{ExternAbi, all_names}; pub use layout::{FIRST_VARIANT, FieldIdx, LayoutCalculator, LayoutCalculatorError, VariantIdx}; #[cfg(feature = "nightly")] pub use layout::{Layout, TyAbiInterface, TyAndLayout}; +pub use wrapping_range::WrappingRange; #[derive(Clone, Copy, PartialEq, Eq, Default)] #[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))] @@ -1475,152 +1477,6 @@ impl Primitive { } } -/// Inclusive wrap-around range of valid values, that is, if -/// start > end, it represents `start..=MAX`, followed by `0..=end`. -/// -/// That is, for an i8 primitive, a range of `254..=2` means following -/// sequence: -/// -/// 254 (-2), 255 (-1), 0, 1, 2 -/// -/// This is intended specifically to mirror LLVM’s `!range` metadata semantics. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "nightly", derive(StableHash))] -pub struct WrappingRange { - pub start: u128, - pub end: u128, -} - -impl WrappingRange { - fn debug_as(&self, size: Size, is_signed: bool) -> impl fmt::Debug { - let range = *self; - fmt::from_fn(move |f| { - if range == WrappingRange::full(size) { - // This is intentionally not using `is_full_for` so that we ensure - // different values always debug-print differently. - // We don't need the full details when it's the canonical full range, - // but if one is looking at the debug output it might be that seeing - // `u8 is (..=0) | (1..)` instead of `u8 is ..` is the information - // you needed because the problem is that despite being *a* full - // range it's not *the* canonical one you expected it was. - f.write_str("..") - } else if is_signed { - let start = size.sign_extend(range.start); - let end = size.sign_extend(range.end); - if start > end { - write!(f, "(..={}) | ({}..)", end, start) - } else { - write!(f, "{}..={}", start, end) - } - } else { - write!(f, "{:?}", range) - } - }) - } - - pub fn full(size: Size) -> Self { - Self { start: 0, end: size.unsigned_int_max() } - } - - /// Returns `true` if `v` is contained in the range. - #[inline(always)] - pub fn contains(&self, v: u128) -> bool { - if self.start <= self.end { - self.start <= v && v <= self.end - } else { - self.start <= v || v <= self.end - } - } - - /// Returns `true` if all the values in `other` are contained in this range, - /// when the values are considered as having width `size`. - #[inline(always)] - pub fn contains_range(&self, other: Self, size: Size) -> bool { - if self.is_full_for(size) { - true - } else { - let trunc = |x| size.truncate(x); - - let delta = self.start; - let max = trunc(self.end.wrapping_sub(delta)); - - let other_start = trunc(other.start.wrapping_sub(delta)); - let other_end = trunc(other.end.wrapping_sub(delta)); - - // Having shifted both input ranges by `delta`, now we only need to check - // whether `0..=max` contains `other_start..=other_end`, which can only - // happen if the other doesn't wrap since `self` isn't everything. - (other_start <= other_end) && (other_end <= max) - } - } - - /// Returns `self` with replaced `start` - #[inline(always)] - fn with_start(mut self, start: u128) -> Self { - self.start = start; - self - } - - /// Returns `self` with replaced `end` - #[inline(always)] - fn with_end(mut self, end: u128) -> Self { - self.end = end; - self - } - - /// Returns `true` if `size` completely fills the range. - /// - /// Note that this is *not* the same as `self == WrappingRange::full(size)`. - /// Niche calculations can produce full ranges which are not the canonical one; - /// for example `Option>` gets `valid_range: (..=0) | (1..)`. - #[inline] - fn is_full_for(&self, size: Size) -> bool { - let max_value = size.unsigned_int_max(); - debug_assert!(self.start <= max_value && self.end <= max_value); - self.start == (self.end.wrapping_add(1) & max_value) - } - - /// Checks whether this range is considered non-wrapping when the values are - /// interpreted as *unsigned* numbers of width `size`. - /// - /// Returns `Ok(true)` if there's no wrap-around, `Ok(false)` if there is, - /// and `Err(..)` if the range is full so it depends how you think about it. - #[inline] - pub fn no_unsigned_wraparound(&self, size: Size) -> Result { - if self.is_full_for(size) { Err(..) } else { Ok(self.start <= self.end) } - } - - /// Checks whether this range is considered non-wrapping when the values are - /// interpreted as *signed* numbers of width `size`. - /// - /// This is heavily dependent on the `size`, as `100..=200` does wrap when - /// interpreted as `i8`, but doesn't when interpreted as `i16`. - /// - /// Returns `Ok(true)` if there's no wrap-around, `Ok(false)` if there is, - /// and `Err(..)` if the range is full so it depends how you think about it. - #[inline] - pub fn no_signed_wraparound(&self, size: Size) -> Result { - if self.is_full_for(size) { - Err(..) - } else { - let start: i128 = size.sign_extend(self.start); - let end: i128 = size.sign_extend(self.end); - Ok(start <= end) - } - } -} - -impl fmt::Debug for WrappingRange { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.start > self.end { - write!(fmt, "(..={}) | ({}..)", self.end, self.start)?; - } else { - write!(fmt, "{}..={}", self.start, self.end)?; - } - Ok(()) - } -} - /// Information about one scalar component of a Rust type. #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(StableHash))] diff --git a/compiler/rustc_abi/src/wrapping_range.rs b/compiler/rustc_abi/src/wrapping_range.rs new file mode 100644 index 0000000000000..ecdc7dae88a67 --- /dev/null +++ b/compiler/rustc_abi/src/wrapping_range.rs @@ -0,0 +1,209 @@ +use std::fmt; +use std::ops::RangeFull; + +use crate::Size; +#[cfg(feature = "nightly")] +use crate::StableHash; + +/// Inclusive wrap-around range of valid values, that is, if +/// start > end, it represents `start..=MAX`, followed by `0..=end`. +/// +/// That is, for an i8 primitive, a range of `254..=2` means following +/// sequence: +/// +/// 254 (-2), 255 (-1), 0, 1, 2 +/// +/// This is intended specifically to mirror LLVM’s `!range` metadata semantics. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(StableHash))] +pub struct WrappingRange { + pub start: u128, + pub end: u128, +} + +impl WrappingRange { + pub(crate) fn debug_as(&self, size: Size, is_signed: bool) -> impl fmt::Debug { + let range = *self; + fmt::from_fn(move |f| { + if range == WrappingRange::full(size) { + // This is intentionally not using `is_full_for` so that we ensure + // different values always debug-print differently. + // We don't need the full details when it's the canonical full range, + // but if one is looking at the debug output it might be that seeing + // `u8 is (..=0) | (1..)` instead of `u8 is ..` is the information + // you needed because the problem is that despite being *a* full + // range it's not *the* canonical one you expected it was. + f.write_str("..") + } else if is_signed { + let start = size.sign_extend(range.start); + let end = size.sign_extend(range.end); + if start > end { + write!(f, "(..={}) | ({}..)", end, start) + } else { + write!(f, "{}..={}", start, end) + } + } else { + write!(f, "{:?}", range) + } + }) + } + + pub fn full(size: Size) -> Self { + Self { start: 0, end: size.unsigned_int_max() } + } + + /// Returns `true` if `v` is contained in the range. + #[inline(always)] + pub fn contains(&self, v: u128) -> bool { + if self.start <= self.end { + self.start <= v && v <= self.end + } else { + self.start <= v || v <= self.end + } + } + + /// Returns `true` if all the values in `other` are contained in this range, + /// when the values are considered as having width `size`. + #[inline(always)] + pub fn contains_range(&self, other: Self, size: Size) -> bool { + if self.is_full_for(size) { + true + } else { + let trunc = |x| size.truncate(x); + + let delta = self.start; + let max = trunc(self.end.wrapping_sub(delta)); + + let other_start = trunc(other.start.wrapping_sub(delta)); + let other_end = trunc(other.end.wrapping_sub(delta)); + + // Having shifted both input ranges by `delta`, now we only need to check + // whether `0..=max` contains `other_start..=other_end`, which can only + // happen if the other doesn't wrap since `self` isn't everything. + (other_start <= other_end) && (other_end <= max) + } + } + + /// Returns `self` with replaced `start` + #[inline(always)] + pub(crate) fn with_start(mut self, start: u128) -> Self { + self.start = start; + self + } + + /// Returns `self` with replaced `end` + #[inline(always)] + pub(crate) fn with_end(mut self, end: u128) -> Self { + self.end = end; + self + } + + /// The wrapping distance from `self.start` to `self.end`. + fn width(&self, size: Size) -> u128 { + size.truncate(u128::wrapping_sub(self.end, self.start)) + } + + /// Returns `true` if `size` completely fills the range. + /// + /// Note that this is *not* the same as `self == WrappingRange::full(size)`. + /// Niche calculations can produce full ranges which are not the canonical one; + /// for example `Option>` gets `valid_range: (..=0) | (1..)`. + #[inline] + pub fn is_full_for(&self, size: Size) -> bool { + let max_value = size.unsigned_int_max(); + debug_assert!(self.start <= max_value && self.end <= max_value); + self.start == (self.end.wrapping_add(1) & max_value) + } + + /// Checks whether this range is considered non-wrapping when the values are + /// interpreted as *unsigned* numbers of width `size`. + /// + /// Returns `Ok(true)` if there's no wrap-around, `Ok(false)` if there is, + /// and `Err(..)` if the range is full so it depends how you think about it. + #[inline] + pub fn no_unsigned_wraparound(&self, size: Size) -> Result { + if self.is_full_for(size) { Err(..) } else { Ok(self.start <= self.end) } + } + + /// Checks whether this range is considered non-wrapping when the values are + /// interpreted as *signed* numbers of width `size`. + /// + /// This is heavily dependent on the `size`, as `100..=200` does wrap when + /// interpreted as `i8`, but doesn't when interpreted as `i16`. + /// + /// Returns `Ok(true)` if there's no wrap-around, `Ok(false)` if there is, + /// and `Err(..)` if the range is full so it depends how you think about it. + #[inline] + pub fn no_signed_wraparound(&self, size: Size) -> Result { + if self.is_full_for(size) { + Err(..) + } else { + let start: i128 = size.sign_extend(self.start); + let end: i128 = size.sign_extend(self.end); + Ok(start <= end) + } + } + + /// Returns a `WrappingRange` that contains all of the values from the iterator, + /// when they're treated as values `size` wide. + /// + /// # Examples + /// + /// + /// ``` + /// use rustc_abi::{Size, WrappingRange}; + /// + /// let range = WrappingRange::smallest_range_containing([2, 6, 12, 4], Size::from_bytes(2)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 2, end: 12 }); + /// + /// let range = WrappingRange::smallest_range_containing(0..=127, Size::from_bytes(1)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 0, end: 127 }); + /// let range = WrappingRange::smallest_range_containing([129, 128, 127], Size::from_bytes(1)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 127, end: 129 }); + /// + /// // The size matters because it changes where the wrapping can happen: + /// let range = WrappingRange::smallest_range_containing([1, 254], Size::from_bytes(1)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 254, end: 1 }); + /// let range = WrappingRange::smallest_range_containing([1, 254], Size::from_bytes(4)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 1, end: 254 }); + /// + /// // Both `100..=228` and `..=228 | 100..` are the same size, but we pick the one without zero. + /// let range = WrappingRange::smallest_range_containing([100, 228], Size::from_bytes(1)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 100, end: 228 }); + /// // These 4 values are evenly spaced so all 4 candidate ranges have length 193: + /// // `(..=32) | (96..)`, `(..=96) | (160..)`, `(..=160) | (224..)`, and `32..=224`. + /// // We pick the last one as the only one that doesn't contain zero. + /// let range = WrappingRange::smallest_range_containing([0xA0, 0xE0, 0x20, 0x60], Size::from_bytes(1)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 0x20, end: 0xE0 }); + /// ``` + pub fn smallest_range_containing( + values: impl IntoIterator, + size: Size, + ) -> Option { + let mut values: Vec<_> = values.into_iter().collect(); + let umax = size.unsigned_int_max(); + for value in &values { + debug_assert!(*value <= umax, "Value {value:?} is too big for {size:?}"); + } + values.sort_unstable(); + + // Having sorted all the values, every element is a possible start point for the + // range of values, up to the previous element (wrapping around the end of the vec). + // Look at all those candidates and pick the one that's as narrow as possible. + let pairs = std::iter::zip(values.iter().copied(), values.iter().copied().cycle().skip(1)); + let ranges = pairs.map(|(end, start)| WrappingRange { start, end }); + let smallest_range = ranges.min_by_key(|r| (r.width(size), r.start)); + smallest_range + } +} + +impl fmt::Debug for WrappingRange { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.start > self.end { + write!(fmt, "(..={}) | ({}..)", self.end, self.start)?; + } else { + write!(fmt, "{}..={}", self.start, self.end)?; + } + Ok(()) + } +} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e9d3610cdde69..7784fc17828aa 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2286,7 +2286,7 @@ impl<'hir> LoweringContext<'_, 'hir> { colon_span: param.colon_span.map(|s| self.lower_span(s)), source, }; - self.lower_attrs(hir_id, param_attrs, param_span, Target::from_generic_param(¶m)); + self.lower_attrs(hir_id, param_attrs, param_span, Target::from(¶m)); param } diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 68d8160db14e1..e20d4585925c3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -123,8 +123,7 @@ impl CombineAttributeParser for RustcDumpLayoutParser { Allow(Target::TyAlias), ]); - const TEMPLATE: AttributeTemplate = - template!(List: &["abi", "align", "size", "homogenous_aggregate", "debug"]); + const TEMPLATE: AttributeTemplate = template!(List: &["abi", "align", "size", "homogenous_aggregate", "largest_niche", "debug"]); const STABILITY: AttributeStability = unstable!(rustc_attrs); fn extend( @@ -150,6 +149,7 @@ impl CombineAttributeParser for RustcDumpLayoutParser { sym::backend_repr => RustcDumpLayoutKind::BackendRepr, sym::debug => RustcDumpLayoutKind::Debug, sym::homogeneous_aggregate => RustcDumpLayoutKind::HomogenousAggregate, + sym::largest_niche => RustcDumpLayoutKind::LargestNiche, sym::size => RustcDumpLayoutKind::Size, _ => { cx.adcx().expected_specific_argument( diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index 624f4ffea6ffc..8f265bad9728e 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -35,5 +35,4 @@ tracing = "0.1" [features] # tidy-alphabetical-start -llvm_enzyme = [] # tidy-alphabetical-end diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index c42ad17498136..8beb69f7b37d1 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -43,7 +43,6 @@ tracing = "0.1" [features] # tidy-alphabetical-start check_only = ["rustc_llvm/check_only"] -llvm_enzyme = [] llvm_offload = [] # tidy-alphabetical-end diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index b2d22876c1858..70d48def59e91 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( ); } - if cfg!(feature = "llvm_enzyme") && enable_ad && !thin { + if 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/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 7c2e09227e46b..9988ccfaee052 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -75,15 +75,9 @@ enum TTLocation { Callsite, } -#[cfg_attr(not(feature = "llvm_enzyme"), allow(unused))] pub(crate) fn add_tt<'tcx, 'll>(cx: &FullCx<'ll, 'tcx>, fn_def: &'ll Value, tt: FncTree) { - // TypeTree processing uses functions from Enzyme, which we might not have available if we did - // not build this compiler with `llvm_enzyme`. This feature is not strictly necessary, but - // skipping this function increases the chance that Enzyme fails to compile some code. - // FIXME(autodiff): In the future we should conditionally run this function even without the - // `llvm_enzyme` feature, in case that libEnzyme was provided via rustup. - #[cfg(not(feature = "llvm_enzyme"))] - return; + // TypeTree processing uses functions from Enzyme. This feature is not strictly necessary, + // but skipping this function increases the chance that Enzyme fails to compile some code. let tcx = cx.tcx; if !tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) { diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index aeb2063e76960..c7d3e4fae3fc5 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -58,7 +58,6 @@ ctrlc = "3.4.4" # tidy-alphabetical-start check_only = ['rustc_interface/check_only'] llvm = ['rustc_interface/llvm'] -llvm_enzyme = ['rustc_interface/llvm_enzyme'] llvm_offload = ['rustc_interface/llvm_offload'] max_level_info = ['rustc_log/max_level_info'] rustc_randomized_layouts = [ diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fd94f64ef4269..8747d5aced73f 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -799,6 +799,7 @@ pub enum RustcDumpLayoutKind { BackendRepr, Debug, HomogenousAggregate, + LargestNiche, Size, } diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index a59b30ebf2748..2097e860468ec 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -7,7 +7,7 @@ use rustc_ast::{AssocItemKind, ForeignItemKind, ast}; use rustc_macros::StableHash; use crate::def::DefKind; -use crate::{Item, ItemKind, TraitItem, TraitItemKind, hir}; +use crate::{self as hir, ItemKind, TraitItemKind}; #[derive(Copy, Clone, PartialEq, Debug, Eq, StableHash)] pub enum GenericParamKind { @@ -124,51 +124,6 @@ impl Target { | Target::Break => false, } } - - pub fn from_item(item: &Item<'_>) -> Target { - match item.kind { - ItemKind::ExternCrate(..) => Target::ExternCrate, - ItemKind::Use(..) => Target::Use, - ItemKind::Static { .. } => Target::Static, - ItemKind::Const(..) => Target::Const, - ItemKind::Fn { .. } => Target::Fn, - ItemKind::Macro(..) => Target::MacroDef, - ItemKind::Mod(..) => Target::Mod, - ItemKind::ForeignMod { .. } => Target::ForeignMod, - ItemKind::GlobalAsm { .. } => Target::GlobalAsm, - ItemKind::TyAlias(..) => Target::TyAlias, - ItemKind::Enum(..) => Target::Enum, - ItemKind::Struct(..) => Target::Struct, - ItemKind::Union(..) => Target::Union, - ItemKind::Trait { .. } => Target::Trait, - ItemKind::TraitAlias(..) => Target::TraitAlias, - ItemKind::Impl(imp_) => Target::Impl { of_trait: imp_.of_trait.is_some() }, - } - } - - // FIXME: For now, should only be used with def_kinds from ItemIds - pub fn from_def_kind(def_kind: DefKind) -> Target { - match def_kind { - DefKind::ExternCrate => Target::ExternCrate, - DefKind::Use => Target::Use, - DefKind::Static { .. } => Target::Static, - DefKind::Const { .. } => Target::Const, - DefKind::Fn => Target::Fn, - DefKind::Macro(..) => Target::MacroDef, - DefKind::Mod => Target::Mod, - DefKind::ForeignMod => Target::ForeignMod, - DefKind::GlobalAsm => Target::GlobalAsm, - DefKind::TyAlias => Target::TyAlias, - DefKind::Enum => Target::Enum, - DefKind::Struct => Target::Struct, - DefKind::Union => Target::Union, - DefKind::Trait => Target::Trait, - DefKind::TraitAlias => Target::TraitAlias, - DefKind::Impl { of_trait } => Target::Impl { of_trait }, - _ => panic!("impossible case reached"), - } - } - pub fn from_ast_item(item: &ast::Item) -> Target { match item.kind { ast::ItemKind::ExternCrate(..) => Target::ExternCrate, @@ -203,43 +158,6 @@ impl Target { } } - pub fn from_trait_item(trait_item: &TraitItem<'_>) -> Target { - match trait_item.kind { - TraitItemKind::Const(..) => Target::AssocConst, - TraitItemKind::Fn(_, hir::TraitFn::Required(_)) => { - Target::Method(MethodKind::Trait { body: false }) - } - TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => { - Target::Method(MethodKind::Trait { body: true }) - } - TraitItemKind::Type(..) => Target::AssocTy, - } - } - - pub fn from_foreign_item(foreign_item: &hir::ForeignItem<'_>) -> Target { - match foreign_item.kind { - hir::ForeignItemKind::Fn(..) => Target::ForeignFn, - hir::ForeignItemKind::Static(..) => Target::ForeignStatic, - hir::ForeignItemKind::Type => Target::ForeignTy, - } - } - - pub fn from_generic_param(generic_param: &hir::GenericParam<'_>) -> Target { - match generic_param.kind { - hir::GenericParamKind::Type { default, .. } => Target::GenericParam { - kind: GenericParamKind::Type, - has_default: default.is_some(), - }, - hir::GenericParamKind::Lifetime { .. } => { - Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: false } - } - hir::GenericParamKind::Const { default, .. } => Target::GenericParam { - kind: GenericParamKind::Const, - has_default: default.is_some(), - }, - } - } - pub fn from_assoc_item_kind(kind: &ast::AssocItemKind, assoc_ctxt: AssocCtxt) -> Target { match kind { AssocItemKind::Const(_) => Target::AssocConst, @@ -381,3 +299,93 @@ impl Target { } } } + +impl From<&hir::ForeignItem<'_>> for Target { + fn from(foreign_item: &hir::ForeignItem<'_>) -> Target { + match foreign_item.kind { + hir::ForeignItemKind::Fn(..) => Target::ForeignFn, + hir::ForeignItemKind::Static(..) => Target::ForeignStatic, + hir::ForeignItemKind::Type => Target::ForeignTy, + } + } +} + +impl From<&hir::GenericParam<'_>> for Target { + fn from(generic_param: &hir::GenericParam<'_>) -> Target { + match generic_param.kind { + hir::GenericParamKind::Type { default, .. } => Target::GenericParam { + kind: GenericParamKind::Type, + has_default: default.is_some(), + }, + hir::GenericParamKind::Lifetime { .. } => { + Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: false } + } + hir::GenericParamKind::Const { default, .. } => Target::GenericParam { + kind: GenericParamKind::Const, + has_default: default.is_some(), + }, + } + } +} + +impl From<&hir::TraitItem<'_>> for Target { + fn from(trait_item: &hir::TraitItem<'_>) -> Target { + match trait_item.kind { + TraitItemKind::Const(..) => Target::AssocConst, + TraitItemKind::Fn(_, hir::TraitFn::Required(_)) => { + Target::Method(MethodKind::Trait { body: false }) + } + TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => { + Target::Method(MethodKind::Trait { body: true }) + } + TraitItemKind::Type(..) => Target::AssocTy, + } + } +} + +impl From for Target { + fn from(def_kind: DefKind) -> Target { + match def_kind { + DefKind::ExternCrate => Target::ExternCrate, + DefKind::Use => Target::Use, + DefKind::Static { .. } => Target::Static, + DefKind::Const { .. } => Target::Const, + DefKind::Fn => Target::Fn, + DefKind::Macro(..) => Target::MacroDef, + DefKind::Mod => Target::Mod, + DefKind::ForeignMod => Target::ForeignMod, + DefKind::GlobalAsm => Target::GlobalAsm, + DefKind::TyAlias => Target::TyAlias, + DefKind::Enum => Target::Enum, + DefKind::Struct => Target::Struct, + DefKind::Union => Target::Union, + DefKind::Trait => Target::Trait, + DefKind::TraitAlias => Target::TraitAlias, + DefKind::Impl { of_trait } => Target::Impl { of_trait }, + _ => panic!("impossible case reached"), + } + } +} + +impl From<&hir::Item<'_>> for Target { + fn from(item: &hir::Item<'_>) -> Target { + match item.kind { + ItemKind::ExternCrate(..) => Target::ExternCrate, + ItemKind::Use(..) => Target::Use, + ItemKind::Static { .. } => Target::Static, + ItemKind::Const(..) => Target::Const, + ItemKind::Fn { .. } => Target::Fn, + ItemKind::Macro(..) => Target::MacroDef, + ItemKind::Mod(..) => Target::Mod, + ItemKind::ForeignMod { .. } => Target::ForeignMod, + ItemKind::GlobalAsm { .. } => Target::GlobalAsm, + ItemKind::TyAlias(..) => Target::TyAlias, + ItemKind::Enum(..) => Target::Enum, + ItemKind::Struct(..) => Target::Struct, + ItemKind::Union(..) => Target::Union, + ItemKind::Trait { .. } => Target::Trait, + ItemKind::TraitAlias(..) => Target::TraitAlias, + ItemKind::Impl(imp_) => Target::Impl { of_trait: imp_.of_trait.is_some() }, + } + } +} diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 9c115736a3d4f..4a272867046b4 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -57,6 +57,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'] llvm_offload = ['rustc_codegen_llvm/llvm_offload'] # tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 22b643e74e582..4aa9f4d2eb1b0 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -828,6 +828,7 @@ fn test_unstable_options_tracking_hash() { tracked!(function_sections, Some(false)); tracked!(hint_mostly_unused, true); tracked!(human_readable_cgu_names, true); + tracked!(implicit_sysroot_deps, false); tracked!(incremental_ignore_spans, true); tracked!(indirect_branch_cs_prefix, true); tracked!(inline_mir, Some(true)); diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 3fb35d48513ad..85c658e10d41d 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -69,9 +69,11 @@ impl abi::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. + /// discriminant range and `#[repr]` attribute. + /// + /// To represent the way the values were written in the rust source, min and max + /// are in different types. It's thus possible to pass in an unrepresentable range, + /// and the method will panic in those cases. /// /// This is the basis for computing the type of the *tag* of an enum (which can be smaller than /// the type of the *discriminant*, which is determined by [`ReprOptions::discr_type`]). @@ -79,15 +81,24 @@ impl abi::Integer { tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, repr: &ReprOptions, - min: i128, - max: i128, + min_negative: i128, + max_positive: u128, ) -> (abi::Integer, bool) { + assert!( + min_negative >= 0 || max_positive <= i128::MAX.cast_unsigned(), + "No type can represent the full range of {min_negative}..={max_positive}", + ); + // 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 = abi::Integer::fit_unsigned(cmp::max(min as u128, max as u128)); - let signed_fit = cmp::max(abi::Integer::fit_signed(min), abi::Integer::fit_signed(max)); + let unsigned_fit = + abi::Integer::fit_unsigned(cmp::max(min_negative.cast_unsigned(), max_positive)); + let signed_fit = cmp::max( + abi::Integer::fit_signed(min_negative), + abi::Integer::fit_signed(max_positive.cast_signed()), + ); if let Some(ity) = repr.int { let discr = abi::Integer::from_attr(&tcx, ity); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e1147fcbe0167..5c4b2d74bfe9e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1690,7 +1690,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { } } - let target = Target::from_item(item); + let target = Target::from(item); self.check_attributes(item.hir_id(), item.span, target, Some(item)); intravisit::walk_item(self, item) } @@ -1706,13 +1706,13 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { } fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) { - let target = Target::from_generic_param(generic_param); + let target = Target::from(generic_param); self.check_attributes(generic_param.hir_id, generic_param.span, target, None); intravisit::walk_generic_param(self, generic_param) } fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) { - let target = Target::from_trait_item(trait_item); + let target = Target::from(trait_item); self.check_attributes(trait_item.hir_id(), trait_item.span, target, None); intravisit::walk_trait_item(self, trait_item) } @@ -1728,7 +1728,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { } fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) { - let target = Target::from_foreign_item(f_item); + let target = Target::from(f_item); self.check_attributes(f_item.hir_id(), f_item.span, target, None); intravisit::walk_foreign_item(self, f_item) } diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 19f2ca3af9232..85235906c38f0 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -85,6 +85,9 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, kinds: &[RustcDumpLa ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, typing_env }); format!("homogeneous_aggregate: {data:?}") } + RustcDumpLayoutKind::LargestNiche => { + format!("largest_niche: {:?}", ty_layout.largest_niche) + } RustcDumpLayoutKind::Size => format!("size: {:?}", ty_layout.size), }; tcx.dcx().span_err(span, message); 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 f55ceb6ffa6f2..350718b4f077a 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 @@ -129,34 +129,8 @@ fn encode_const<'tcx>( // Element type s.push_str(&encode_ty(tcx, cv.ty, dict, options)); - // The only allowed types of const values are bool, u8, u16, u32, - // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The - // bool value false is encoded as 0 and true as 1. - match cv.ty.kind() { - ty::Int(ity) => { - let bits = cv - .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) - .expect("expected monomorphic const in cfi"); - let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; - if val < 0 { - s.push('n'); - } - let _ = write!(s, "{val}"); - } - ty::Uint(_) => { - let val = cv - .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) - .expect("expected monomorphic const in cfi"); - let _ = write!(s, "{val}"); - } - ty::Bool => { - let val = cv.try_to_bool().expect("expected monomorphic const in cfi"); - let _ = write!(s, "{val}"); - } - _ => { - bug!("encode_const: unexpected type `{:?}`", cv.ty); - } - } + // Element value + s.push_str(&encode_const_value(tcx, cv, dict, options)); } _ => { @@ -172,6 +146,127 @@ fn encode_const<'tcx>( s } +/// Encodes a const value using the Itanium C++ ABI as the element value of a literal argument (see +/// ). +fn encode_const_value<'tcx>( + tcx: TyCtxt<'tcx>, + cv: ty::Value<'tcx>, + dict: &mut FxHashMap, usize>, + options: EncodeTyOptions, +) -> String { + let mut s = String::new(); + + match cv.ty.kind() { + // Primitive types + + // The bool value false is encoded as 0 and true as 1. + ty::Bool => { + let val = cv.try_to_bool().expect("expected monomorphic const in cfi"); + s.push(if val { '1' } else { '0' }); + } + + // Integer values are encoded as their decimal values, with negative values preceded by n. + ty::Int(ity) => { + let bits = cv + .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) + .expect("expected monomorphic const in cfi"); + let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; + if val < 0 { + s.push('n'); + } + let _ = write!(s, "{}", val.unsigned_abs()); + } + + ty::Uint(..) => { + let val = cv + .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) + .expect("expected monomorphic const in cfi"); + let _ = write!(s, "{val}"); + } + + // char values are encoded as their Unicode scalar values (i.e., as their decimal u32 + // values). + ty::Char => { + let val = cv + .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) + .expect("expected monomorphic const in cfi"); + let _ = write!(s, "{val}"); + } + + // str values are encoded as their UTF-8 encodings in hexadecimal. + ty::Str => { + // Hide the str type behind a reference for try_to_raw_bytes (i.e., the valtree of a + // str value is the valtree of its reference). + let ref_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, cv.ty); + let cv = ty::Value { ty: ref_ty, valtree: cv.valtree }; + let bytes = cv.try_to_raw_bytes(tcx).expect("expected monomorphic const in cfi"); + for byte in bytes { + let _ = write!(s, "{byte:02x}"); + } + } + + // Sequence types + // Array, slice, and tuple values are encoded as their element values as literal arguments. + ty::Array(..) | ty::Slice(..) | ty::Tuple(..) => { + for field in cv.to_branch() { + let ty::ConstKind::Value(field_cv) = field.kind() else { + bug!("encode_const_value: unexpected kind `{:?}`", field.kind()); + }; + s.push_str(&encode_const(tcx, *field, field_cv.ty, dict, options)); + } + } + + // User-defined types + // Struct and enum values are encoded as their field values as literal arguments, preceded + // by V for enum values. + ty::Adt(adt_def, ..) => { + let contents = cv.destructure_adt_const(); + if adt_def.is_enum() { + let _ = write!(s, "V{}", contents.variant.as_u32()); + } + for field in contents.fields { + let ty::ConstKind::Value(field_cv) = field.kind() else { + bug!("encode_const_value: unexpected kind `{:?}`", field.kind()); + }; + s.push_str(&encode_const(tcx, *field, field_cv.ty, dict, options)); + } + } + + // Pointer types + // Reference values are encoded as the values of their referents (i.e., the valtree of a + // reference value is the valtree of its referent). + ty::Ref(_, ty0, ..) => { + let cv = ty::Value { ty: *ty0, valtree: cv.valtree }; + s.push_str(&encode_const_value(tcx, cv, dict, options)); + } + + // Unexpected types + ty::Float(..) + | ty::Never + | ty::Foreign(..) + | ty::Pat(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::RawPtr(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::UnsafeBinder(..) + | ty::Param(..) + | ty::Alias(..) + | ty::Bound(..) + | ty::Error(..) + | ty::Infer(..) + | ty::Placeholder(..) => { + bug!("encode_const_value: unexpected type `{:?}`", cv.ty); + } + } + + s +} + /// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for /// Rust types that are not used at the FFI boundary. fn encode_fnsig<'tcx>( diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index bf1e1f5f12811..b2290f92aae12 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -12,6 +12,7 @@ use crate::search_paths::{PathKind, SearchPath}; pub struct FileSearch { cli_search_paths: Vec, tlib_path: SearchPath, + use_implicit_sysroot_deps: bool, } impl FileSearch { @@ -20,16 +21,27 @@ impl FileSearch { } pub fn search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator { + // If the crate is `PathKind::Crate` (a top level dependency) + // and `-Z implicit-sysroot-deps=false`, then don't include the sysroot in the search paths. + let exclude_sysroot = kind.matches(PathKind::Crate) && !self.use_implicit_sysroot_deps; + let maybe_tlib = (!exclude_sysroot).then_some(&self.tlib_path); + self.cli_search_paths .iter() .filter(move |sp| sp.kind.matches(kind)) - .chain(std::iter::once(&self.tlib_path)) + .chain(maybe_tlib.into_iter()) } - pub fn new(cli_search_paths: &[SearchPath], tlib_path: &SearchPath, target: &Target) -> Self { + pub fn new( + cli_search_paths: &[SearchPath], + tlib_path: &SearchPath, + target: &Target, + use_implicit_sysroot_deps: bool, + ) -> Self { let this = FileSearch { cli_search_paths: cli_search_paths.to_owned(), tlib_path: tlib_path.clone(), + use_implicit_sysroot_deps, }; this.refine(&["lib", &target.staticlib_prefix, &target.dll_prefix]) } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5b71c0435185a..ecfff0e64baf1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2531,6 +2531,8 @@ options! { "display unnamed regions as `'`, using a non-ident unique id (default: no)"), ignore_directory_in_diagnostics_source_blocks: Vec = (Vec::new(), parse_string_push, [UNTRACKED], "do not display the source code block in diagnostics for files in the directory"), + implicit_sysroot_deps: bool = (true, parse_bool, [TRACKED], + "allows rust to search sysroot for a crate's dependencies (default: yes)"), incremental_ignore_spans: bool = (false, parse_bool, [TRACKED], "ignore spans during ICH computation -- used for testing (default: no)"), incremental_info: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index eebead6fc1f47..e7f335e01d2f1 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1335,9 +1335,18 @@ pub fn build_session( }); let asm_arch = if target.allow_asm { InlineAsmArch::from_arch(&target.arch) } else { None }; - let target_filesearch = - filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target); - let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host); + let target_filesearch = filesearch::FileSearch::new( + &sopts.search_paths, + &target_tlib_path, + &target, + sopts.unstable_opts.implicit_sysroot_deps, + ); + let host_filesearch = filesearch::FileSearch::new( + &sopts.search_paths, + &host_tlib_path, + &host, + sopts.unstable_opts.implicit_sysroot_deps, + ); let timings = TimingSectionHandler::new(sopts.json_timings); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 39e6f7445b09c..ed12adf71cff2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1174,6 +1174,7 @@ symbols! { lang, lang_items, large_assignments, + largest_niche, last, lasx, late_bound_turbofishing, diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 7a53fe1e0cfc8..350c2cb24876e 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -1,3 +1,5 @@ +use std::range::{RangeFrom, RangeToInclusive}; + use hir::def_id::DefId; use rustc_abi as abi; use rustc_abi::Integer::{I8, I32}; @@ -715,12 +717,13 @@ fn layout_of_uncached<'tcx>( // UnsafeCell and UnsafePinned both disable niche optimizations let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned(); - let discr_range_of_repr = - |min, max| abi::Integer::discr_range_of_repr(tcx, ty, &def.repr(), min, max); + let discr_range_of_repr = |min: RangeFrom, max: RangeToInclusive| { + abi::Integer::discr_range_of_repr(tcx, ty, &def.repr(), min.start, max.last) + }; let discriminants_iter = || { def.is_enum() - .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128))) + .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val))) .into_flat_iter() }; diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 5421b1d2a2652..3f5f9f454a99d 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -23,7 +23,6 @@ optimize_for_size = [] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = [] -llvm_enzyme = [] [lints.rust.unexpected_cfgs] level = "warn" @@ -39,7 +38,6 @@ check-cfg = [ 'cfg(target_has_reliable_f16_math)', 'cfg(target_has_reliable_f128)', 'cfg(target_has_reliable_f128_math)', - 'cfg(llvm_enzyme)', # Prevents use of a static variable for providing platform specific RawOsError # functionality 'cfg(no_io_statics)', diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 38340cd6aba73..424587bf75566 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -125,8 +125,6 @@ optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] # a borrow error occurs debug_refcell = ["core/debug_refcell"] -llvm_enzyme = ["core/llvm_enzyme"] - # Enable using raw-dylib for Windows imports. # This will eventually be the default. windows_raw_dylib = ["windows-link/windows_raw_dylib"] diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index b2069ef6a613b..10562389d62d3 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -32,4 +32,3 @@ optimize_for_size = ["std/optimize_for_size"] panic-unwind = ["std/panic-unwind"] profiler = ["dep:profiler_builtins"] windows_raw_dylib = ["std/windows_raw_dylib"] -llvm_enzyme = ["std/llvm_enzyme"] diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 57d7518792926..a2184f75487fe 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -13,6 +13,8 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::{env, fs, iter}; +use build_helper::git::get_closest_upstream_commit; + use crate::core::build_steps::compile::{ArtifactKeepMode, Std, run_cargo}; use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags}; @@ -4613,3 +4615,90 @@ impl CommandLineStep for RemoteTestClientTests { ); } } + +fn check_if_cargo_semver_checks_is_installed(builder: &Builder<'_>) -> bool { + command("cargo") + .allow_failure() + .arg("semver-checks") + .arg("--version") + // Cache the output to avoid running this command more than once (per builder). + .cached() + .run_capture_stdout(builder) + .is_success() +} + +/// Run cargo-semver-checks on the standard library and compare its API +/// versus a previous baseline, using rustdoc JSON data. +/// +/// Fails if a semver-breaking change is detected. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StdSemverCheck { + build_compiler: Compiler, + target: TargetSelection, + /// The baseline commit that we are comparing the local stdlib API against. + commit: String, +} + +impl CommandLineStep for StdSemverCheck { + type Output = (); + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("std-semver-check") + } + + fn make_run(run: RunConfig<'_>) { + if !check_if_cargo_semver_checks_is_installed(run.builder) { + panic!("cargo-semver-checks was not found, please install it"); + } + + let baseline_commit = match get_closest_upstream_commit( + Some(&run.builder.config.src), + &run.builder.config.git_config(), + run.builder.config.ci_env, + ) { + Ok(Some(commit)) => commit, + Ok(None) => { + panic!("No baseline parent commit found for std-semver-check"); + } + Err(error) => { + panic!("Cannot get baseline parent commit for std-semver-check: {error:?}"); + } + }; + + run.builder.ensure(Self { + build_compiler: run.builder.compiler_for_std(run.builder.top_stage), + target: run.target, + commit: baseline_commit, + }); + } + + fn run(self, builder: &Builder<'_>) { + let Some(docs_dir) = builder.config.download_std_json_docs(self.target, &self.commit) + else { + return; + }; + + let directory = builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler( + self.build_compiler, + self.target, + DocumentationFormat::Json, + )); + let baseline_dir = docs_dir.join("share").join("doc").join("rust").join("json"); + + for library in ["core", "alloc", "std"] { + println!("Checking semver compatibility of {library}"); + let mut cmd = command("cargo"); + cmd.arg("semver-checks") + .arg("-Z") + .arg("unstable-options") + .arg("--stability-aware") + .arg("--release-type") + .arg("minor") + .arg("--current-rustdoc") + .arg(directory.join(format!("{library}.json"))) + .arg("--baseline-rustdoc") + .arg(baseline_dir.join(format!("{library}.json"))); + cmd.run(builder); + } + } +} diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_semver_check.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_semver_check.snap new file mode 100644 index 0000000000000..0a2720e55b785 --- /dev/null +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_semver_check.snap @@ -0,0 +1,7 @@ +--- +source: src/bootstrap/src/core/builder/cli_paths/tests.rs +expression: test std-semver-check +--- +[Test] test::StdSemverCheck + targets: [aarch64-unknown-linux-gnu] + - Set({test::std-semver-check}) diff --git a/src/bootstrap/src/core/builder/cli_paths/tests.rs b/src/bootstrap/src/core/builder/cli_paths/tests.rs index 465a370ad69f8..45655b71e611a 100644 --- a/src/bootstrap/src/core/builder/cli_paths/tests.rs +++ b/src/bootstrap/src/core/builder/cli_paths/tests.rs @@ -170,6 +170,7 @@ declare_tests!( (x_test_librustdoc_rustdoc_html, "test librustdoc rustdoc-html"), (x_test_rustdoc, "test rustdoc"), (x_test_rustdoc_html, "test rustdoc-html"), + (x_test_semver_check, "test std-semver-check"), (x_test_skip_coverage, "test --skip=coverage"), (x_test_skip_coverage_map, "test --skip=coverage-map"), (x_test_skip_coverage_run, "test --skip=coverage-run"), diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 50188887a5314..ce476883c8839 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -974,6 +974,7 @@ impl<'a> Builder<'a> { test::RunMake, test::RunMakeCargo, test::BuildStd, + test::StdSemverCheck, test::IntrinsicTest, ), Kind::Miri => describe!(test::Crate), diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index e6d55fd530adb..d3bc5718b171e 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -173,6 +173,30 @@ impl Config { ); } + pub(crate) fn download_std_json_docs( + &self, + target: TargetSelection, + commit: &str, + ) -> Option { + if self.dry_run() { + return None; + } + + self.do_if_verbose(|| println!("using downloaded std json docs from CI (commit {commit})")); + + let version = self.artifact_version_part(commit); + download_component( + DownloadContext::from(self), + &self.out, + DownloadSource::CI, + format!("rust-docs-json-{version}-{target}.tar.xz"), + "rust-docs-json-preview", + // When using DownloadSource::CI, the key is assumed to end with -llvm-assertions + &format!("{commit}-{}", self.llvm_assertions), + "ci-docs-json", + ) + } + fn download_toolchain( &self, version: &str, @@ -785,11 +809,11 @@ fn download_component<'a>( prefix: &str, key: &str, destination: &str, -) { +) -> Option { let dwn_ctx = dwn_ctx.as_ref(); if dwn_ctx.exec_ctx.dry_run() { - return; + return None; } let cache_dst = @@ -834,8 +858,7 @@ fn download_component<'a>( let sha256 = dwn_ctx.stage0_metadata.checksums_sha256.get(&url).expect(&error); if tarball.exists() { if verify(dwn_ctx.exec_ctx, &tarball, sha256) { - unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); - return; + return Some(unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix)); } else { dwn_ctx.exec_ctx.do_if_verbose(|| { println!( @@ -848,8 +871,7 @@ fn download_component<'a>( } Some(sha256) } else if tarball.exists() { - unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); - return; + return Some(unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix)); } else { None }; @@ -872,7 +894,7 @@ download-rustc = false panic!("failed to verify {}", tarball.display()); } - unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); + Some(unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix)) } pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) -> bool { @@ -916,7 +938,7 @@ pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) - verified } -fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str) { +fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str) -> PathBuf { eprintln!("extracting {} to {}", tarball.display(), dst.display()); if !dst.exists() { t!(fs::create_dir_all(dst)); @@ -979,6 +1001,7 @@ fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str if dst_dir.exists() { t!(fs::remove_dir_all(&dst_dir), format!("failed to remove {}", dst_dir.display())); } + dst.to_path_buf() } fn download_file<'a>( diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index df35face8b5d8..3babca128d471 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -848,10 +848,6 @@ impl Build { features.insert("compiler-builtins-mem"); } - if self.config.llvm_enzyme { - features.insert("llvm_enzyme"); - } - features.into_iter().collect::>().join(" ") } @@ -874,9 +870,6 @@ 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"); - } if self.config.llvm_offload { features.push("llvm_offload"); } diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 87a52eee49b85..5cc1b3a1da85d 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -165,11 +165,6 @@ pub fn changes_since(git_dir: &Path, base: &str, paths: &[&str]) -> Result String { @@ -209,11 +204,6 @@ fn get_latest_upstream_commit_that_modified_files( &escape_email_git_regex(git_config.git_merge_commit_email), ]); - // Also search for temporary bors account - if git_config.git_merge_commit_email != TEMPORARY_BORS_EMAIL { - git.args(["--author", &escape_email_git_regex(TEMPORARY_BORS_EMAIL)]); - } - if !target_paths.is_empty() { git.arg("--").args(target_paths); } @@ -263,11 +253,6 @@ pub fn get_closest_upstream_commit( base, ]); - // Also search for temporary bors account - if config.git_merge_commit_email != TEMPORARY_BORS_EMAIL { - git.args(["--author", &escape_email_git_regex(TEMPORARY_BORS_EMAIL)]); - } - let output = output_result(&mut git)?.trim().to_owned(); if output.is_empty() { Ok(None) } else { Ok(Some(output)) } } diff --git a/src/ci/citool/Cargo.lock b/src/ci/citool/Cargo.lock index a208de47256ca..185eecbde1a02 100644 --- a/src/ci/citool/Cargo.lock +++ b/src/ci/citool/Cargo.lock @@ -66,9 +66,9 @@ checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "askama" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57" +checksum = "f1bf825125edd887a019d0a3a837dcc5499a68b0d034cc3eb594070c3e18addc" dependencies = [ "askama_macros", "itoa", @@ -79,12 +79,13 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37" +checksum = "e1c7065972a130eafa84215f21352ae15b4a7393da48c1f5e103904490736738" dependencies = [ "askama_parser", "basic-toml", + "glob", "memchr", "proc-macro2", "quote", @@ -96,18 +97,18 @@ dependencies = [ [[package]] name = "askama_macros" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b" +checksum = "0e23b1d2c4bd39a41971f6124cef4cc6fd0540913ecb90919b69ab3bbe44ae1a" dependencies = [ "askama_derive", ] [[package]] name = "askama_parser" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c" +checksum = "7db09fde9143e7ac4513358fb32ee32847125b63b18ea715afd487956da715da" dependencies = [ "rustc-hash", "serde", @@ -380,6 +381,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + [[package]] name = "glob-match" version = "0.2.1" @@ -1105,9 +1112,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.6" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] diff --git a/src/ci/citool/src/test_dashboard.rs b/src/ci/citool/src/test_dashboard.rs index c9de38852e5a8..bfcb2ba790e3e 100644 --- a/src/ci/citool/src/test_dashboard.rs +++ b/src/ci/citool/src/test_dashboard.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs::File; use std::io::BufWriter; use std::path::{Path, PathBuf}; @@ -17,12 +17,14 @@ pub fn generate_test_dashboard( output_dir: &Path, ) -> anyhow::Result<()> { let metrics = download_auto_job_metrics(&db, None, current)?; - let suites = gather_test_suites(&metrics); + let mut suites = gather_test_suites(&metrics); std::fs::create_dir_all(output_dir)?; + let jobsets = assign_jobsets(&mut suites); + let test_count = suites.test_count(); - write_page(output_dir, "index.html", &TestSuitesPage { suites, test_count })?; + write_page(output_dir, "index.html", &TestSuitesPage { suites, test_count, jobsets })?; Ok(()) } @@ -33,6 +35,45 @@ fn write_page(dir: &Path, name: &str, template: &T) -> anyhow::Resu Ok(()) } +struct JobSets { + sets: Vec<(u32, Vec)>, +} + +fn assign_jobsets(suites: &mut TestSuites) -> JobSets { + let mut jobsets: HashMap, u32> = HashMap::new(); + + fn visit(jobsets: &mut HashMap, u32>, group: &mut TestGroup) { + for (_, test) in &mut group.root_tests { + for (_, results) in &mut test.revisions { + let mut jobset: HashSet = HashSet::new(); + for test in &results.passed { + jobset.insert(test.job.to_string()); + } + let mut jobset: Vec = jobset.into_iter().collect(); + jobset.sort(); + + let jobset_count = jobsets.len() as u32; + let jobset_id = jobsets.entry(jobset).or_insert_with(|| jobset_count); + results.passed_jobset = Some(*jobset_id); + } + } + for (_, group) in &mut group.groups { + visit(jobsets, group); + } + } + for suite in &mut suites.suites { + visit(&mut jobsets, &mut suite.group); + } + + let mut jobsets: Vec<(u32, Vec)> = jobsets.into_iter().map(|(k, v)| (v, k)).collect(); + jobsets.sort_unstable_by(|a, b| a.0.cmp(&b.0)); + for (_, set) in &mut jobsets { + set.sort_unstable(); + } + + JobSets { sets: jobsets } +} + fn gather_test_suites(job_metrics: &HashMap) -> TestSuites<'_> { struct CoarseTestSuite<'a> { tests: BTreeMap>, @@ -68,10 +109,9 @@ fn gather_test_suites(job_metrics: &HashMap) -> TestSuites< .tests .entry(test_name.clone()) .or_insert_with(|| Test { revisions: Default::default() }); - let variant_entry = test_entry - .revisions - .entry(variant_name) - .or_insert_with(|| TestResults { passed: vec![], ignored: vec![] }); + let variant_entry = test_entry.revisions.entry(variant_name).or_insert_with(|| { + TestResults { passed: vec![], passed_jobset: None, ignored: vec![] } + }); match test.outcome { TestOutcome::Passed => { @@ -161,6 +201,8 @@ struct TestSuite<'a> { struct TestResults<'a> { passed: Vec>, + /// An id representing a set of jobs on which this test has passed. + passed_jobset: Option, ignored: Vec>, } @@ -213,4 +255,5 @@ impl<'a> TestGroup<'a> { struct TestSuitesPage<'a> { suites: TestSuites<'a>, test_count: u64, + jobsets: JobSets, } diff --git a/src/ci/citool/templates/test_group.askama b/src/ci/citool/templates/test_group.askama index 1a72c47d3788a..2e9a8327c8537 100644 --- a/src/ci/citool/templates/test_group.askama +++ b/src/ci/citool/templates/test_group.askama @@ -1,5 +1,5 @@ -{% macro test_result(r) -%} -passed: {{ r.passed.len() }}, ignored: {{ r.ignored.len() }} +{% macro test_result(r, jobset) -%} +passed: {{ r.passed.len() }}, ignored: {{ r.ignored.len() }} {%- endmacro %}
  • @@ -24,12 +24,12 @@ passed: {{ r.passed.len() }}, ignored: {{ r.ignored.len() }} {% for (name, test) in root_tests %}
  • {% if let Some(result) = test.single_test() %} - {{ name }} ({% call test_result(result) %}{% endcall %}) + {{ name }} ({% call test_result(result, result.passed_jobset.as_ref().unwrap()) %}{% endcall %}) {% else %} {{ name }} ({{ test.revisions.len() }} revision{{ test.revisions.len() | pluralize }})
      {% for (revision, result) in test.revisions %} -
    • #{{ revision }} ({% call test_result(result) %}{% endcall %})
    • +
    • #{{ revision }} ({% call test_result(result, result.passed_jobset.as_ref().unwrap()) %}{% endcall %})
    • {% endfor %}
    {% endif %} diff --git a/src/ci/citool/templates/test_suites.askama b/src/ci/citool/templates/test_suites.askama index 4997f6a3f1c9a..59ba2b7e86916 100644 --- a/src/ci/citool/templates/test_suites.askama +++ b/src/ci/citool/templates/test_suites.askama @@ -20,11 +20,26 @@ the count includes all combinations of "stage" x "target" x "CI job where the te -
      +
        {% for suite in suites.suites %} {{ suite.group|safe }} {% endfor %}
      +
      +

      Job sets

      +
      + {% for (id, jobs) in jobsets.sets %} + {% if !jobs.is_empty() %} +
      J{{ id }}
      +
        + {% for job in jobs %} +
      • {{ job }}
      • + {% endfor %} +
      + {% endif %} + {% endfor %} +
      +
      {% endblock %} @@ -34,6 +49,9 @@ h1 { color: #333333; margin-bottom: 30px; } +a:visited { + color: blue; +} .summary { display: flex; @@ -55,10 +73,14 @@ ul { padding-left: 0; } -li { +.tests li { list-style: none; padding-left: 20px; } +.jobsets li { + margin-left: 30px; +} + summary { margin-bottom: 5px; padding: 6px; diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 26985e67abb6e..c7572029936b5 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -74,6 +74,10 @@ implemented as a hard-coded list, these traits have a special marker attribute on them: `#[doc(notable_trait)]`. This means that you can apply this attribute to your own trait to include it in the "Notable traits" dialog in documentation. +In addition to the "Notable traits" dialog, every type that implements a +`#[doc(notable_trait)]` trait renders a colored badge for that trait at the top +of its page, making the relationship easy to spot when browsing the type. + The `#[doc(notable_trait)]` attribute currently requires the `#![feature(doc_notable_trait)]` feature gate. For more information, see [its chapter in the Unstable Book][unstable-notable_trait] and [its tracking issue][issue-notable_trait]. diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 4f5fa2b51cf85..1c31c5b81eb11 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -41,7 +41,7 @@ mod write_shared; use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::VecDeque; +use std::collections::{BTreeMap, VecDeque}; use std::fmt::{self, Display as _, Write}; use std::iter::Peekable; use std::path::PathBuf; @@ -1664,6 +1664,15 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> } } +/// `Box` has pass-through impls for `Read`, `Write`, `Iterator`, and `Future` when the +/// boxed type implements one of those. We don't want to treat every `Box` return +/// as being notably an `Iterator` (etc), though, so we exempt it. `Pin` has the same +/// issue, with a pass-through impl for `Future`. +fn is_notable_trait_passthrough(did: DefId, cx: &Context<'_>) -> bool { + let lang_items = cx.tcx().lang_items(); + Some(did) == lang_items.owned_box() || Some(did) == lang_items.pin_type() +} + fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option { if ty.is_unit() { // Very common fast path. @@ -1672,13 +1681,7 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option(tys: impl Iterator, cx: &Cont serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail") } +pub(crate) struct NotableTraitBadge { + pub name: String, + pub full_path: String, + /// Relative URL to the trait page, or `None` if it cannot be linked. + pub href: Option, +} + +/// Returns all `#[doc(notable_trait)]` traits that `item` implements, to be +/// rendered as badges at the top of the item's page. +pub(crate) fn notable_trait_badges(item: &clean::Item, cx: &Context<'_>) -> Vec { + let tcx = cx.tcx(); + if let Some(def_id) = item.def_id() + && !is_notable_trait_passthrough(def_id, cx) + && let Some(impls) = cx.cache().impls.get(&def_id) + { + impls + .iter() + .map(Impl::inner_impl) + .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive) + .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(tcx) + { + let name = tcx.item_name(trait_did).to_string(); + let (full_path, href) = match href(trait_did, cx) { + Ok(info) => (join_path_syms(&info.rust_path), Some(info.url)), + Err(_) => (tcx.def_path_str(trait_did), None), + }; + Some((name.clone(), NotableTraitBadge { name, full_path, href })) + } else { + None + } + }) + .collect::>() + .into_values() + .collect() + } else { + Vec::new() + } +} + #[derive(Clone, Copy, Debug)] struct ImplRenderingParameters { show_def_docs: bool, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 7c1cc4ba7f9d9..7aeed50fdff51 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1,6 +1,8 @@ use std::borrow::Cow; use std::cmp::Ordering; +use std::collections::hash_map::DefaultHasher; use std::fmt::{self, Display, Write as _}; +use std::hash::{Hash, Hasher}; use std::iter; use askama::Template; @@ -38,7 +40,7 @@ use crate::html::format::{ }; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::sidebar::filters; -use crate::html::render::{document_full, document_item_info}; +use crate::html::render::{document_full, document_item_info, notable_trait_badges}; use crate::html::url_parts_builder::UrlPartsBuilder; const ITEM_TABLE_OPEN: &str = "
      "; @@ -51,6 +53,15 @@ struct PathComponent { name: Symbol, } +struct NotableTraitBadgeVars { + name: String, + full_path: String, + /// Relative URL to the trait page, or `None` when not linkable. + href: Option, + /// Index of the `.notable-trait-badge-{n}` color class. + color_index: u8, +} + #[derive(Template)] #[template(path = "print_item.html")] struct ItemVars<'a> { @@ -59,6 +70,7 @@ struct ItemVars<'a> { item_type: &'a str, path_components: Vec, stability_since_raw: &'a str, + notable_trait_badges: Vec, src_href: Option<&'a str>, } @@ -112,6 +124,25 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Disp let src_href = if cx.info.include_sources && !item.is_primitive() { cx.src_href(item) } else { None }; + let notable_trait_badges: Vec = notable_trait_badges(item, cx) + .into_iter() + .map(|info| { + // Stable per-trait color from a hash of the trait path so the + // same trait gets the same badge color across pages. + // This won't be stable between releases though. + let mut h = DefaultHasher::new(); + info.full_path.hash(&mut h); + const BADGE_COLORS: u8 = 6; + let color_index = (h.finish() as u8) % BADGE_COLORS; + NotableTraitBadgeVars { + name: info.name, + full_path: info.full_path, + href: info.href, + color_index, + } + }) + .collect(); + let path_components = if item.is_fake_item() { vec![] } else { @@ -135,6 +166,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Disp item_type: &item.type_().to_string(), path_components, stability_since_raw: &stability_since_raw, + notable_trait_badges, src_href: src_href.as_deref(), }; diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 363cec3116a96..b769238d91a8b 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -141,6 +141,12 @@ nav.sub { --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); --sidebar-resizer-hover: hsl(207, 90%, 66%); --sidebar-resizer-active: hsl(207, 90%, 54%); + --notable-badge-pink: oklch(0.88 0.21 0); + --notable-badge-red: oklch(0.88 0.21 40); + --notable-badge-orange: oklch(0.88 0.21 70); + --notable-badge-green: oklch(0.88 0.21 150); + --notable-badge-blue: oklch(0.88 0.21 240); + --notable-badge-violet: oklch(0.88 0.21 300); } /* End theme: light */ @@ -252,6 +258,12 @@ nav.sub { --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); --sidebar-resizer-hover: hsl(207, 30%, 54%); --sidebar-resizer-active: hsl(207, 90%, 54%); + --notable-badge-pink: oklch(0.55 0.21 0); + --notable-badge-red: oklch(0.55 0.21 40); + --notable-badge-orange: oklch(0.55 0.21 70); + --notable-badge-green: oklch(0.55 0.21 150); + --notable-badge-blue: oklch(0.55 0.21 240); + --notable-badge-violet: oklch(0.55 0.21 300); } /* End theme: dark */ } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index d154ebba60434..83b36ffbe7618 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1647,6 +1647,36 @@ so that we can apply CSS-filters to change the arrow color in themes */ font-size: initial; } +.notable-trait-badge-container { + padding: 0.5rem 0; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.notable-trait-badge-container > a { + display: flex; + align-items: center; + width: fit-content; + height: 1.5rem; + padding: 0 0.5rem; + border-radius: 0.75rem; + font-size: 1rem; + font-weight: normal; + color: var(--main-color); +} + +.notable-trait-badge-container > a:hover { + text-decoration: none; +} + +.notable-trait-badge-container > .badge-0 { background: var(--notable-badge-pink); } +.notable-trait-badge-container > .badge-1 { background: var(--notable-badge-red); } +.notable-trait-badge-container > .badge-2 { background: var(--notable-badge-orange); } +.notable-trait-badge-container > .badge-3 { background: var(--notable-badge-green); } +.notable-trait-badge-container > .badge-4 { background: var(--notable-badge-blue); } +.notable-trait-badge-container > .badge-5 { background: var(--notable-badge-violet); } + .rightside { padding-left: 12px; float: right; @@ -3280,6 +3310,12 @@ by default. --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); --sidebar-resizer-hover: hsl(207, 90%, 66%); --sidebar-resizer-active: hsl(207, 90%, 54%); + --notable-badge-pink: oklch(0.88 0.21 0); + --notable-badge-red: oklch(0.88 0.21 40); + --notable-badge-orange: oklch(0.88 0.21 70); + --notable-badge-green: oklch(0.88 0.21 150); + --notable-badge-blue: oklch(0.88 0.21 240); + --notable-badge-violet: oklch(0.88 0.21 300); } /* End theme: light */ @@ -3390,6 +3426,12 @@ by default. --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); --sidebar-resizer-hover: hsl(207, 30%, 54%); --sidebar-resizer-active: hsl(207, 90%, 54%); + --notable-badge-pink: oklch(0.55 0.21 0); + --notable-badge-red: oklch(0.55 0.21 40); + --notable-badge-orange: oklch(0.55 0.21 70); + --notable-badge-green: oklch(0.55 0.21 150); + --notable-badge-blue: oklch(0.55 0.21 240); + --notable-badge-violet: oklch(0.55 0.21 300); } /* End theme: dark */ @@ -3504,6 +3546,12 @@ Original by Dempfi (https://github.com/dempfi/ayu) --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0); --sidebar-resizer-hover: hsl(34, 50%, 33%); --sidebar-resizer-active: hsl(34, 100%, 66%); + --notable-badge-pink: oklch(0.49 0.21 0); + --notable-badge-red: oklch(0.49 0.21 40); + --notable-badge-orange: oklch(0.49 0.21 70); + --notable-badge-green: oklch(0.49 0.21 150); + --notable-badge-blue: oklch(0.49 0.21 240); + --notable-badge-violet: oklch(0.49 0.21 300); } :root[data-theme="ayu"] h1, diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 640fd3dfee498..71764832abb45 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -20,6 +20,15 @@

      {# #} {# #} + {% if !notable_trait_badges.is_empty() %} +
      + {% for badge in notable_trait_badges.iter() %} + {{badge.name}} + {% endfor %} +
      + {% endif %} {% if !stability_since_raw.is_empty() %} {{ stability_since_raw|safe +}} {% endif %} diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index aeaea8ca9bb5b..342743d21340f 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -38,6 +38,7 @@ macro_rules! location { #[rustfmt::skip] const LICENSES: &[&str] = &[ // tidy-alphabetical-start + "(MIT OR Apache-2.0) AND MIT", "0BSD OR MIT OR Apache-2.0", // adler2 license "Apache-2.0 / MIT", "Apache-2.0 OR ISC OR MIT", diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs index cf26c17af1ed3..98591b0d4f1d2 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs @@ -5,10 +5,15 @@ //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer #![crate_type = "lib"] +#![feature(adt_const_params)] #![feature(type_alias_impl_trait)] +#![feature(unsized_const_params)] +#![allow(incomplete_features)] extern crate core; +use std::marker::ConstParamTy; + pub type Type1 = impl Send; #[define_opaque(Type1)] @@ -27,6 +32,52 @@ pub fn foo2(_: Type1, _: Type1) {} pub fn foo3(_: Type1, _: Type1, _: Type1) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +#[derive(PartialEq, Eq, ConstParamTy)] +pub struct Struct1 { + pub x: u16, + pub y: u16, +} + +#[derive(PartialEq, Eq, ConstParamTy)] +pub enum Enum1 { + Variant1, + Variant2(u8), +} + +pub struct BoolHolder; +pub struct IntHolder; +pub struct CharHolder; +pub struct StrHolder; +pub struct StructHolder; +pub struct EnumHolder; +pub struct ArrayHolder; +pub struct TupleHolder; + +pub fn foo4(_: &BoolHolder) {} +// CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo5(_: &IntHolder<-1>) {} +// CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo6(_: &CharHolder<'x'>) {} +// CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo7(_: &StrHolder<"hello">) {} +// CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo8(_: &StructHolder<{ Struct1 { x: 1, y: 2 } }>) {} +// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo9(_: &EnumHolder<{ Enum1::Variant2(5) }>) {} +// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo10(_: &ArrayHolder<{ [3, 4] }>) {} +// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo11(_: &TupleHolder<{ (6, true) }>) {} +// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EEE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EES2_E"} // CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EES2_S2_E"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}10BoolHolderILb1EEEE"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}9IntHolderILu3i32n1EEEE"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}10CharHolderILu4char120EEEE"} +// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}9StrHolderILu3refIu3strE68656c6c6fEEEE"} +// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}12StructHolderILu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}7Struct1Lu3u161ELS0_2EEEEE"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}10EnumHolderILu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Enum1V1Lu2u85EEEEE"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}11ArrayHolderILA2u3u16LS_3ELS_4EEEEE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}11TupleHolderILu5tupleIu3u16bELS_6ELb1EEEEE"} diff --git a/tests/run-make/implicit-sysroot-deps/bar.rs b/tests/run-make/implicit-sysroot-deps/bar.rs new file mode 100644 index 0000000000000..ececc69fe9514 --- /dev/null +++ b/tests/run-make/implicit-sysroot-deps/bar.rs @@ -0,0 +1,4 @@ +#![feature(no_core)] +#![no_core] +#![no_std] +extern crate baz; diff --git a/tests/run-make/implicit-sysroot-deps/baz.rs b/tests/run-make/implicit-sysroot-deps/baz.rs new file mode 100644 index 0000000000000..ef13d56b7d4f0 --- /dev/null +++ b/tests/run-make/implicit-sysroot-deps/baz.rs @@ -0,0 +1,3 @@ +#![feature(no_core)] +#![no_core] +#![no_std] diff --git a/tests/run-make/implicit-sysroot-deps/foo.rs b/tests/run-make/implicit-sysroot-deps/foo.rs new file mode 100644 index 0000000000000..90d13bd02b1db --- /dev/null +++ b/tests/run-make/implicit-sysroot-deps/foo.rs @@ -0,0 +1,4 @@ +#![feature(no_core)] +#![no_core] +#![no_std] +extern crate bar; diff --git a/tests/run-make/implicit-sysroot-deps/rmake.rs b/tests/run-make/implicit-sysroot-deps/rmake.rs new file mode 100644 index 0000000000000..a748bda4db271 --- /dev/null +++ b/tests/run-make/implicit-sysroot-deps/rmake.rs @@ -0,0 +1,38 @@ +use run_make_support::rfs::create_dir_all; +use run_make_support::{rust_lib_name, rustc, target}; + +// Tests `-Zimplicit-sysroot-deps=false` with arbitrary crates passed via `--extern` +// See `tests/ui/crate-loading` for tests with the standard library + +fn main() { + // Create test sysroot + let test_sysroot = format!("./testsysroot/lib/rustlib/{}/lib/", target()); + create_dir_all(&test_sysroot); + + // Layout: + // - Foo depends directly on Bar + // - Bar depends directly on Baz + // - Baz can be found in the sysroot. + + // 1) Depending transitively on a lib in the sysroot resolves + // fine with `-Zimplicit-sysroot-deps=false` + rustc().input("baz.rs").crate_type("lib").out_dir(test_sysroot).run(); + rustc().input("bar.rs").crate_type("lib").sysroot("./testsysroot").run(); + rustc() + .input("foo.rs") + .crate_type("lib") + .extern_("bar", rust_lib_name("bar")) + .sysroot("./testsysroot") + .arg("-Zimplicit-sysroot-deps=false") + .run(); + + // 2) Depending directly on a lib in the sysroot does not resolve + // implicitly with `-Zimplicit-sysroot-deps=false` + rustc() + .input("bar.rs") + .crate_type("lib") + .sysroot("./testsysroot") + .arg("-Zimplicit-sysroot-deps=false") + .run_fail() + .assert_stderr_contains("can't find crate for `baz`"); +} diff --git a/tests/rustdoc-html/notable-trait/auxiliary/notable-dep.rs b/tests/rustdoc-html/notable-trait/auxiliary/notable-dep.rs new file mode 100644 index 0000000000000..391e6771443c9 --- /dev/null +++ b/tests/rustdoc-html/notable-trait/auxiliary/notable-dep.rs @@ -0,0 +1,4 @@ +#![feature(doc_notable_trait)] + +#[doc(notable_trait)] +pub trait Spaceship {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-generic.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-generic.rs new file mode 100644 index 0000000000000..723f3fed6c5a5 --- /dev/null +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-generic.rs @@ -0,0 +1,13 @@ +#![feature(doc_notable_trait)] +#![crate_name = "foo"] + +#[doc(notable_trait)] +pub trait Labeled {} + +pub trait Bound {} + +// A conditional impl: the badge is rendered unconditionally even though the +// impl only holds for `T: Bound`. +//@ has 'foo/struct.Wrapper.html' '//div[@class="notable-trait-badge-container"]/a[@href="trait.Labeled.html"]' 'Labeled' +pub struct Wrapper(pub T); +impl Labeled for Wrapper {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs new file mode 100644 index 0000000000000..225572513c40f --- /dev/null +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs @@ -0,0 +1,15 @@ +#![feature(doc_notable_trait, negative_impls)] +#![crate_name = "foo"] + +#[doc(notable_trait)] +pub trait Neg {} +#[doc(notable_trait)] +pub trait Pos {} + +// A negative impl must not produce a badge. +//@ has 'foo/struct.T.html' +//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Pos.html"]' 'Pos' +pub struct T; +impl !Neg for T {} +impl Pos for T {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs new file mode 100644 index 0000000000000..9bb3226099a51 --- /dev/null +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs @@ -0,0 +1,16 @@ +#![feature(doc_notable_trait)] +#![crate_name = "foo"] + +#[doc(notable_trait)] +pub trait Base {} + +pub trait Derived: Base {} + +//@ has 'foo/struct.S.html' +// Implementing `Derived` requires implementing the notable supertrait `Base`, +// so its badge shows up. +//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Base.html"]' 'Base' +pub struct S; +impl Base for S {} +impl Derived for S {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs new file mode 100644 index 0000000000000..de7008cf5ab0c --- /dev/null +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs @@ -0,0 +1,17 @@ +//@ aux-build:notable-dep.rs + +#![crate_name = "foo"] + +extern crate notable_dep; + +// A notable trait from a dependency that was compiled but not documented is +// unlinkable: the badge is still emitted but rendered as plain text. +use notable_dep::Spaceship; + +//@ has 'foo/struct.Rocket.html' +// The badge is present... +//@ has - '//div[@class="notable-trait-badge-container"]/a' 'Spaceship' +// ...but unlinked: no badge carries an `href`. +//@ count - '//div[@class="notable-trait-badge-container"]/a[@href]' 0 +pub struct Rocket; +impl Spaceship for Rocket {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable.rs new file mode 100644 index 0000000000000..97526242f3fd1 --- /dev/null +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable.rs @@ -0,0 +1,21 @@ +#![feature(doc_notable_trait)] +#![crate_name = "foo"] + +// Doc-hidden traits don't get badges. +#[doc(notable_trait)] +#[doc(hidden)] +pub trait Hidden {} + +// Private traits don't get badges. +#[doc(notable_trait)] +trait Private {} +#[doc(notable_trait)] +pub trait Public {} + +//@ has 'foo/struct.Foo.html' +//@ count - '//div[@class="notable-trait-badge-container"]' 1 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Public.html"]' 'Public' +pub struct Foo; +impl Hidden for Foo {} +impl Private for Foo {} +impl Public for Foo {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs new file mode 100644 index 0000000000000..b53d989d0f819 --- /dev/null +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs @@ -0,0 +1,25 @@ +#![feature(doc_notable_trait)] +#![crate_name = "foo"] + +#[doc(notable_trait)] +pub trait Labeled {} + +#[doc(notable_trait)] +pub trait AlsoLabeled {} + +pub trait Plain {} + +//@ has 'foo/struct.Tagged.html' +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Labeled.html"][@title="foo::Labeled"]' 'Labeled' +// Badges are sorted by trait name, so `AlsoLabeled` precedes `Labeled`. +//@ has - '//div[@class="notable-trait-badge-container"]/a[1]' 'AlsoLabeled' +//@ has - '//div[@class="notable-trait-badge-container"]/a[2]' 'Labeled' +pub struct Tagged; +impl Labeled for Tagged {} +impl AlsoLabeled for Tagged {} +impl Plain for Tagged {} + +//@ has 'foo/struct.Untagged.html' +//@ count - '//div[@class="notable-trait-badge-container"]' 0 +pub struct Untagged; +impl Plain for Untagged {} diff --git a/tests/ui/const-generics/generic_const_exprs/closure-in-array-len-ice-119316.rs b/tests/ui/const-generics/generic_const_exprs/closure-in-array-len-ice-119316.rs new file mode 100644 index 0000000000000..bb7597afaa47c --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/closure-in-array-len-ice-119316.rs @@ -0,0 +1,11 @@ +//@ edition: 2018 +// regression test for #119316 +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +async fn foo<'a>() { + let _data = &mut [0u8; { N + (|| 42)() }]; + //~^ ERROR cannot find value `N` in this scope +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/closure-in-array-len-ice-119316.stderr b/tests/ui/const-generics/generic_const_exprs/closure-in-array-len-ice-119316.stderr new file mode 100644 index 0000000000000..a98f9a29b0707 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/closure-in-array-len-ice-119316.stderr @@ -0,0 +1,14 @@ +error[E0425]: cannot find value `N` in this scope + --> $DIR/closure-in-array-len-ice-119316.rs:7:30 + | +LL | let _data = &mut [0u8; { N + (|| 42)() }]; + | ^ not found in this scope + | +help: you might be missing a const parameter + | +LL | async fn foo<'a, const N: /* Type */>() { + | +++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/crate-loading/auxiliary/crate-dep-std.rs b/tests/ui/crate-loading/auxiliary/crate-dep-std.rs new file mode 100644 index 0000000000000..5eec10bfc3e4a --- /dev/null +++ b/tests/ui/crate-loading/auxiliary/crate-dep-std.rs @@ -0,0 +1,3 @@ +pub fn bar() { + println!("I depend on std."); +} diff --git a/tests/ui/crate-loading/no-implicit-sysroot-deps-pass.rs b/tests/ui/crate-loading/no-implicit-sysroot-deps-pass.rs new file mode 100644 index 0000000000000..a2ce18597e0b8 --- /dev/null +++ b/tests/ui/crate-loading/no-implicit-sysroot-deps-pass.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ aux-build:crate-dep-std.rs +//@ compile-flags: --crate-type=lib -Zimplicit-sysroot-deps=false -Cpanic=abort + +// This test ensures that `-Zimplicit-sysroot-deps=false` allows loading transitive +// dependencies from the sysroot when required. + +#![feature(no_core)] +#![no_std] +#![no_core] + +extern crate crate_dep_std as foo; +use foo::bar; +pub fn bark() { + bar(); +} diff --git a/tests/ui/crate-loading/no-implicit-sysroot-deps.rs b/tests/ui/crate-loading/no-implicit-sysroot-deps.rs new file mode 100644 index 0000000000000..9a280cc681571 --- /dev/null +++ b/tests/ui/crate-loading/no-implicit-sysroot-deps.rs @@ -0,0 +1,14 @@ +//~ ERROR can't find crate for `std` +//~| NOTE can't find crate +//~| NOTE target may not be installed +//~| HELP consider building the standard library from source with `cargo build -Zbuild-std` +//~| HELP consider downloading the target with + +//@ compile-flags: --target x86_64-unknown-linux-gnu -Z implicit-sysroot-deps=false +//@ needs-llvm-components: x86 + +// This program has an implicit dependency on std, injected by rust. This test ensures that rustc +// does not search in the sysroot for it when `-Zimplicit-sysroot-deps` is false, and that an +// error is thrown when std is not available on any other search paths. + +fn main() {} diff --git a/tests/ui/crate-loading/no-implicit-sysroot-deps.stderr b/tests/ui/crate-loading/no-implicit-sysroot-deps.stderr new file mode 100644 index 0000000000000..688664cad9596 --- /dev/null +++ b/tests/ui/crate-loading/no-implicit-sysroot-deps.stderr @@ -0,0 +1,9 @@ +error[E0463]: can't find crate for `std` + | + = note: the `x86_64-unknown-linux-gnu` target may not be installed + = help: consider downloading the target with `rustup target add x86_64-unknown-linux-gnu` + = help: consider building the standard library from source with `cargo build -Zbuild-std` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0463`. diff --git a/tests/ui/layout/enum-signedness.rs b/tests/ui/layout/enum-signedness.rs new file mode 100644 index 0000000000000..c40955d39ef27 --- /dev/null +++ b/tests/ui/layout/enum-signedness.rs @@ -0,0 +1,114 @@ +//@ only-64bit +#![feature(rustc_attrs)] +#![feature(never_type)] +#![crate_type = "lib"] + +// When picking a representation for things that don't force a particular discriminant type, +// we look at how the value was actually written, which means that "equivalent" things +// actually need to come out different. + +// The tests run only on 64-bit because we have to give things as `isize` in these cases. + +#[rustc_dump_layout(largest_niche)] +#[repr(Rust)] +enum NegativeByteRust { + //~^ ERROR: value: i8, valid_range: 155..=156 + A = -100, + B = -101, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(C)] +enum NegativeByteC { + //~^ ERROR: value: i32, valid_range: 4294967195..=4294967196 + A = -100, + B = -101, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(Rust)] +enum PositiveByteRust { + //~^ ERROR: value: u8, valid_range: 155..=156 + A = 256 - 100, + B = 256 - 101, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(C)] +enum PositiveByteC { + //~^ ERROR: value: u32, valid_range: 155..=156 + A = 256 - 100, + B = 256 - 101, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(Rust)] +enum Negative32BitRust { + //~^ ERROR: value: i32, valid_range: 0..=2147483648 + A = 0, + B = i32::MIN as isize, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(C)] +enum Negative32BitC { + //~^ ERROR: value: i32, valid_range: 0..=2147483648 + A = 0, + B = i32::MIN as isize, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(Rust)] +enum Positive32BitRust { + //~^ ERROR: value: u32, valid_range: 0..=2147483648 + A = 0, + B = i32::MIN.cast_unsigned() as isize, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(C)] +enum Positive32BitC { + //~^ ERROR: value: u32, valid_range: 0..=2147483648 + A = 0, + B = i32::MIN.cast_unsigned() as isize, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(Rust)] +enum Negative64BitRust { + //~^ ERROR: value: i64, valid_range: 9223372036854775808..=9223372036854775809 + A = i64::MIN as isize, + B = i64::MIN as isize + 1, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(C)] +enum Negative64BitC { + //~^ ERROR: value: i64, valid_range: 9223372036854775808..=9223372036854775809 + A = i64::MIN as isize, + //~^ WARN: enum discriminant does not fit into C + //~| WARN: previously accepted + B = i64::MIN as isize + 1, + //~^ WARN: enum discriminant does not fit into C + //~| WARN: previously accepted +} + +#[rustc_dump_layout(largest_niche)] +#[repr(Rust)] +enum Positive64BitRust { + //~^ ERROR: value: u64, valid_range: 9223372036854775806..=9223372036854775807 + A = i64::MAX as isize - 1, + B = i64::MAX as isize, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(C)] +enum Positive64BitC { + //~^ ERROR: value: u64, valid_range: 9223372036854775806..=9223372036854775807 + A = i64::MAX as isize - 1, + //~^ WARN: enum discriminant does not fit into C + //~| WARN: previously accepted + B = i64::MAX as isize, + //~^ WARN: enum discriminant does not fit into C + //~| WARN: previously accepted +} diff --git a/tests/ui/layout/enum-signedness.stderr b/tests/ui/layout/enum-signedness.stderr new file mode 100644 index 0000000000000..3e2b9fa69971f --- /dev/null +++ b/tests/ui/layout/enum-signedness.stderr @@ -0,0 +1,119 @@ +warning: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` + --> $DIR/enum-signedness.rs:88:5 + | +LL | A = i64::MIN as isize, + | ^ + | + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = 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 #124403 + = note: `#[warn(repr_c_enums_larger_than_int)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` + --> $DIR/enum-signedness.rs:91:5 + | +LL | B = i64::MIN as isize + 1, + | ^ + | + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = 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 #124403 + +warning: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` + --> $DIR/enum-signedness.rs:108:5 + | +LL | A = i64::MAX as isize - 1, + | ^ + | + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = 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 #124403 + +warning: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` + --> $DIR/enum-signedness.rs:111:5 + | +LL | B = i64::MAX as isize, + | ^ + | + = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C + = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = 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 #124403 + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i8, valid_range: 155..=156 }) + --> $DIR/enum-signedness.rs:14:1 + | +LL | enum NegativeByteRust { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i32, valid_range: 4294967195..=4294967196 }) + --> $DIR/enum-signedness.rs:22:1 + | +LL | enum NegativeByteC { + | ^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u8, valid_range: 155..=156 }) + --> $DIR/enum-signedness.rs:30:1 + | +LL | enum PositiveByteRust { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u32, valid_range: 155..=156 }) + --> $DIR/enum-signedness.rs:38:1 + | +LL | enum PositiveByteC { + | ^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i32, valid_range: 0..=2147483648 }) + --> $DIR/enum-signedness.rs:46:1 + | +LL | enum Negative32BitRust { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i32, valid_range: 0..=2147483648 }) + --> $DIR/enum-signedness.rs:54:1 + | +LL | enum Negative32BitC { + | ^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u32, valid_range: 0..=2147483648 }) + --> $DIR/enum-signedness.rs:62:1 + | +LL | enum Positive32BitRust { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u32, valid_range: 0..=2147483648 }) + --> $DIR/enum-signedness.rs:70:1 + | +LL | enum Positive32BitC { + | ^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i64, valid_range: 9223372036854775808..=9223372036854775809 }) + --> $DIR/enum-signedness.rs:78:1 + | +LL | enum Negative64BitRust { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i64, valid_range: 9223372036854775808..=9223372036854775809 }) + --> $DIR/enum-signedness.rs:86:1 + | +LL | enum Negative64BitC { + | ^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u64, valid_range: 9223372036854775806..=9223372036854775807 }) + --> $DIR/enum-signedness.rs:98:1 + | +LL | enum Positive64BitRust { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u64, valid_range: 9223372036854775806..=9223372036854775807 }) + --> $DIR/enum-signedness.rs:106:1 + | +LL | enum Positive64BitC { + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors; 4 warnings emitted + diff --git a/tests/ui/layout/enum-unusual-variants.rs b/tests/ui/layout/enum-unusual-variants.rs new file mode 100644 index 0000000000000..a79ae0e1f32cc --- /dev/null +++ b/tests/ui/layout/enum-unusual-variants.rs @@ -0,0 +1,81 @@ +#![feature(rustc_attrs)] +#![feature(never_type)] +#![crate_type = "lib"] + +// Regression test for https://github.com/rust-lang/rust/issues/159438 +// where with `i8` it was getting an unnecessarily-broad `valid_range` + +#[rustc_dump_layout(largest_niche)] +enum With128Variants { + //~^ ERROR: value: u8, valid_range: 0..=127 + _0 = 0, + _1 = 1, + _2 = 2, + // layout doesn't actually care if we elide a bunch here + _125 = 125, + _126 = 126, + _127 = 127, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(i8)] +enum With128VariantsI8 { + //~^ ERROR: value: i8, valid_range: 0..=127 + _0 = 0, + _1 = 1, + _2 = 2, + // layout doesn't actually care if we elide a bunch here + _125 = 125, + _126 = 126, + _127 = 127, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(u8)] +enum With128VariantsU8 { + //~^ ERROR: value: u8, valid_range: 0..=127 + _0 = 0, + _1 = 1, + _2 = 2, + // layout doesn't actually care if we elide a bunch here + _125 = 125, + _126 = 126, + _127 = 127, +} + +// For these either the wrapping or the non-wrapping `valid_range` have the same size, +// but it would be nice to consistently pick the one that leaves `0` unclaimed +// so that it can be used for `None` later. + +#[rustc_dump_layout(largest_niche)] +enum Symmetric { + //~^ ERROR: value: u8, valid_range: 1..=129 + A = 1, + B = 129, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(u8)] +enum SymmetricU8 { + //~^ ERROR: value: u8, valid_range: 1..=129 + A = 1, + B = 129, +} + +// Note that to get this one to work it's essential that the `valid_range` is +// calculated on the *tag* values, because if calculated on the *discriminants* +// the best range is different because of the wider range of `isize`. +#[rustc_dump_layout(largest_niche)] +enum SymmetricSigned { + //~^ ERROR: value: i8, valid_range: 1..=129 + A = -127, + B = 1, +} + +#[rustc_dump_layout(largest_niche)] +#[repr(i8)] +enum SymmetricSignedI8 { + //~^ ERROR: value: i8, valid_range: 1..=129 + A = -127, + B = 1, +} diff --git a/tests/ui/layout/enum-unusual-variants.stderr b/tests/ui/layout/enum-unusual-variants.stderr new file mode 100644 index 0000000000000..4eabd903a0337 --- /dev/null +++ b/tests/ui/layout/enum-unusual-variants.stderr @@ -0,0 +1,44 @@ +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u8, valid_range: 0..=127 }) + --> $DIR/enum-unusual-variants.rs:9:1 + | +LL | enum With128Variants { + | ^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i8, valid_range: 0..=127 }) + --> $DIR/enum-unusual-variants.rs:22:1 + | +LL | enum With128VariantsI8 { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u8, valid_range: 0..=127 }) + --> $DIR/enum-unusual-variants.rs:35:1 + | +LL | enum With128VariantsU8 { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u8, valid_range: 1..=129 }) + --> $DIR/enum-unusual-variants.rs:51:1 + | +LL | enum Symmetric { + | ^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: u8, valid_range: 1..=129 }) + --> $DIR/enum-unusual-variants.rs:59:1 + | +LL | enum SymmetricU8 { + | ^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i8, valid_range: 1..=129 }) + --> $DIR/enum-unusual-variants.rs:69:1 + | +LL | enum SymmetricSigned { + | ^^^^^^^^^^^^^^^^^^^^ + +error: largest_niche: Some(Niche { offset: Size(0 bytes), value: i8, valid_range: 1..=129 }) + --> $DIR/enum-unusual-variants.rs:77:1 + | +LL | enum SymmetricSignedI8 { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/sanitizer/cfi/const-generics.rs b/tests/ui/sanitizer/cfi/const-generics.rs new file mode 100644 index 0000000000000..42fff233dd84b --- /dev/null +++ b/tests/ui/sanitizer/cfi/const-generics.rs @@ -0,0 +1,110 @@ +// Verifies that functions with types with const generics as argument types can +// be called through function pointers. +// +//@ needs-sanitizer-cfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ ignore-backends: gcc +//@ compile-flags: -Ctarget-feature=-crt-static -Ccodegen-units=1 -Clto -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer +//@ run-pass + +#![feature(adt_const_params)] +#![feature(unsized_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct Struct2 { + x: u16, + y: u16, +} + +#[derive(PartialEq, Eq, ConstParamTy)] +enum Enum1 { + Variant1, + Variant2(u8), +} + +struct Struct1([i32; N]); +struct BoolHolder(bool); +struct IntHolder(i32); +struct CharHolder(char); +struct StrHolder(&'static str); +struct StructHolder(Struct2); +struct EnumHolder(Enum1); +struct ArrayHolder([u16; 2]); +struct TupleHolder((u16, bool)); + +fn foo1(x: Struct1<2>) { + assert_eq!(x.0, [1, 2]); +} + +fn foo2(x: &Struct1<4>) { + assert_eq!(x.0, [1, 2, 3, 4]); +} + +fn foo3(x: BoolHolder) { + assert!(x.0); +} + +fn foo4(x: IntHolder<-1>) { + assert_eq!(x.0, -1); +} + +fn foo5(x: CharHolder<'x'>) { + assert_eq!(x.0, 'x'); +} + +fn foo6(x: StrHolder<"hello">) { + assert_eq!(x.0, "hello"); +} + +fn foo7(x: StructHolder<{ Struct2 { x: 1, y: 2 } }>) { + assert_eq!(x.0.x, 1); + assert_eq!(x.0.y, 2); +} + +fn foo8(x: EnumHolder<{ Enum1::Variant1 }>) { + assert!(matches!(x.0, Enum1::Variant1)); +} + +fn foo9(x: EnumHolder<{ Enum1::Variant2(5) }>) { + match x.0 { + Enum1::Variant1 => unreachable!(), + Enum1::Variant2(v) => assert_eq!(v, 5), + } +} + +fn foo10(x: ArrayHolder<{ [3, 4] }>) { + assert_eq!(x.0, [3, 4]); +} + +fn foo11(x: TupleHolder<{ (6, true) }>) { + assert_eq!(x.0, (6, true)); +} + +fn main() { + let f: fn(Struct1<2>) = foo1; + f(Struct1([1, 2])); + let f: fn(&Struct1<4>) = foo2; + f(&Struct1([1, 2, 3, 4])); + let f: fn(BoolHolder) = foo3; + f(BoolHolder(true)); + let f: fn(IntHolder<-1>) = foo4; + f(IntHolder(-1)); + let f: fn(CharHolder<'x'>) = foo5; + f(CharHolder('x')); + let f: fn(StrHolder<"hello">) = foo6; + f(StrHolder("hello")); + let f: fn(StructHolder<{ Struct2 { x: 1, y: 2 } }>) = foo7; + f(StructHolder(Struct2 { x: 1, y: 2 })); + let f: fn(EnumHolder<{ Enum1::Variant1 }>) = foo8; + f(EnumHolder(Enum1::Variant1)); + let f: fn(EnumHolder<{ Enum1::Variant2(5) }>) = foo9; + f(EnumHolder(Enum1::Variant2(5))); + let f: fn(ArrayHolder<{ [3, 4] }>) = foo10; + f(ArrayHolder([3, 4])); + let f: fn(TupleHolder<{ (6, true) }>) = foo11; + f(TupleHolder((6, true))); +} diff --git a/tests/ui/sanitizer/kcfi/const-generics.rs b/tests/ui/sanitizer/kcfi/const-generics.rs new file mode 100644 index 0000000000000..86f487bb9ea1e --- /dev/null +++ b/tests/ui/sanitizer/kcfi/const-generics.rs @@ -0,0 +1,109 @@ +// Verifies that functions with types with const generics as argument types can +// be called through function pointers. +// +//@ needs-sanitizer-kcfi +//@ only-linux +//@ ignore-backends: gcc +//@ compile-flags: -Ctarget-feature=-crt-static -Cpanic=abort -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=kcfi -Cunsafe-allow-abi-mismatch=sanitizer +//@ run-pass + +#![feature(adt_const_params)] +#![feature(unsized_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct Struct2 { + x: u16, + y: u16, +} + +#[derive(PartialEq, Eq, ConstParamTy)] +enum Enum1 { + Variant1, + Variant2(u8), +} + +struct Struct1([i32; N]); +struct BoolHolder(bool); +struct IntHolder(i32); +struct CharHolder(char); +struct StrHolder(&'static str); +struct StructHolder(Struct2); +struct EnumHolder(Enum1); +struct ArrayHolder([u16; 2]); +struct TupleHolder((u16, bool)); + +fn foo1(x: Struct1<2>) { + assert_eq!(x.0, [1, 2]); +} + +fn foo2(x: &Struct1<4>) { + assert_eq!(x.0, [1, 2, 3, 4]); +} + +fn foo3(x: BoolHolder) { + assert!(x.0); +} + +fn foo4(x: IntHolder<-1>) { + assert_eq!(x.0, -1); +} + +fn foo5(x: CharHolder<'x'>) { + assert_eq!(x.0, 'x'); +} + +fn foo6(x: StrHolder<"hello">) { + assert_eq!(x.0, "hello"); +} + +fn foo7(x: StructHolder<{ Struct2 { x: 1, y: 2 } }>) { + assert_eq!(x.0.x, 1); + assert_eq!(x.0.y, 2); +} + +fn foo8(x: EnumHolder<{ Enum1::Variant1 }>) { + assert!(matches!(x.0, Enum1::Variant1)); +} + +fn foo9(x: EnumHolder<{ Enum1::Variant2(5) }>) { + match x.0 { + Enum1::Variant1 => unreachable!(), + Enum1::Variant2(v) => assert_eq!(v, 5), + } +} + +fn foo10(x: ArrayHolder<{ [3, 4] }>) { + assert_eq!(x.0, [3, 4]); +} + +fn foo11(x: TupleHolder<{ (6, true) }>) { + assert_eq!(x.0, (6, true)); +} + +fn main() { + let f: fn(Struct1<2>) = foo1; + f(Struct1([1, 2])); + let f: fn(&Struct1<4>) = foo2; + f(&Struct1([1, 2, 3, 4])); + let f: fn(BoolHolder) = foo3; + f(BoolHolder(true)); + let f: fn(IntHolder<-1>) = foo4; + f(IntHolder(-1)); + let f: fn(CharHolder<'x'>) = foo5; + f(CharHolder('x')); + let f: fn(StrHolder<"hello">) = foo6; + f(StrHolder("hello")); + let f: fn(StructHolder<{ Struct2 { x: 1, y: 2 } }>) = foo7; + f(StructHolder(Struct2 { x: 1, y: 2 })); + let f: fn(EnumHolder<{ Enum1::Variant1 }>) = foo8; + f(EnumHolder(Enum1::Variant1)); + let f: fn(EnumHolder<{ Enum1::Variant2(5) }>) = foo9; + f(EnumHolder(Enum1::Variant2(5))); + let f: fn(ArrayHolder<{ [3, 4] }>) = foo10; + f(ArrayHolder([3, 4])); + let f: fn(TupleHolder<{ (6, true) }>) = foo11; + f(TupleHolder((6, true))); +}