Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1e19b0b
rustc_abi: move WrappingRange to its own file
scottmcm Jul 18, 2026
679bdc1
CFI: Add support for the adt_const_params feature
rcvalle Jul 19, 2026
e278105
WrappingRange: add a constructor from the desired covered values
scottmcm Jul 18, 2026
e5f0680
Stop passing the discriminant as `i128` when `Discr` makes it as `u128`
scottmcm Jul 18, 2026
934f93b
Add some enum niche choice tests
scottmcm Jul 18, 2026
9cbba6e
Fix signedness handling when picking enum ranges
scottmcm Jul 18, 2026
0195461
Adds `-Z implicit-sysroot-deps` boolean flag
CrooseGit Feb 27, 2026
7b0ecd3
Implements `implicit-sysroot-deps=false`
CrooseGit Mar 3, 2026
56c4f45
Adds tests for `implicit-sysroot-deps=false
CrooseGit Mar 3, 2026
fd9a014
Adds more tests for `-Z implicit-syroot-deps=false`
CrooseGit Mar 5, 2026
0ac1461
Show jobs where a given test was executed in `test-dashboard`
Kobzol Jul 27, 2026
e62c86f
Add regression test for closure in array-length const generic
zakrad Jul 24, 2026
1544f42
Add `./x test std-semver-check` test command
Kobzol Jul 21, 2026
73ba963
Add CLI snapshot test for semver check step
Kobzol Jul 27, 2026
b2ed4e8
Review updates
adamgemmell Jul 28, 2026
46ac557
rustdoc: add #[doc(label_trait)] to render a trait badge
ThierryBerger Jun 5, 2026
81983a1
label trait feature now part of notable traits features
ThierryBerger Jun 11, 2026
2131738
experiment with oklch, stricter palette
ThierryBerger Jun 12, 2026
27a005a
pr suggestion, cleaner indentation
ThierryBerger Jun 29, 2026
c7702b6
use option for href in NotableTraitBadgeVars
ThierryBerger Jun 29, 2026
77f575f
use shared function for passing through the notable trait information.
ThierryBerger Jun 29, 2026
5c465a6
use css classes for colors, number down to 6
ThierryBerger Jun 29, 2026
a245f5e
use u8 for badge color index
ThierryBerger Jul 1, 2026
ab8b24c
use let chains
ThierryBerger Jul 7, 2026
5b47394
add more tests about unlinkable private/doc-hidden/undocumented
ThierryBerger Jul 7, 2026
ba87784
Remove llvm_enzyme feature outside of bootstrap
ZuseZ4 Jul 28, 2026
ed6df30
Make notable badge colors theme-dependant
GuillaumeGomez Jul 28, 2026
493ed4a
Make notable trait badge tests a bit "stronger"
GuillaumeGomez Jul 28, 2026
4968453
convert rustc_hir target inherent methods to From impls
mejrs Jul 28, 2026
d40bb6d
add additional license option for third-party dependencies
marcoieni Jul 29, 2026
9523a14
bootstrap: remove temporary bors email lookup
kn1g78 Jul 29, 2026
581f75e
Rollup merge of #158460 - ZuseZ4:simplify-enzyme-feature, r=bjorn3
JonathanBrouwer Jul 29, 2026
bb97a9c
Rollup merge of #159509 - scottmcm:redo-valid-range, r=oli-obk
JonathanBrouwer Jul 29, 2026
1a9b3a7
Rollup merge of #159632 - rcvalle:rust-cfi-fix-159299, r=bjorn3
JonathanBrouwer Jul 29, 2026
14c6694
Rollup merge of #159671 - Kobzol:semver-checks, r=jieyouxu
JonathanBrouwer Jul 29, 2026
ee17d0d
Rollup merge of #157058 - ThierryBerger:rustdoc_label_trait, r=Guilla…
JonathanBrouwer Jul 29, 2026
9ad1558
Rollup merge of #159717 - adamgemmell:dev/reucru01/no-implicit-sysroo…
JonathanBrouwer Jul 29, 2026
11c2b5a
Rollup merge of #159850 - zakrad:regr-test-closure-array-len, r=tiif
JonathanBrouwer Jul 29, 2026
b6f0d80
Rollup merge of #159994 - Kobzol:test-dashboard-jobs, r=jieyouxu
JonathanBrouwer Jul 29, 2026
ee0e6cf
Rollup merge of #160110 - mejrs:target_impls, r=oli-obk
JonathanBrouwer Jul 29, 2026
34f9778
Rollup merge of #160123 - marcoieni:tidy-add-license, r=jieyouxu
JonathanBrouwer Jul 29, 2026
28018c2
Rollup merge of #160131 - kn1g78:remove-temporary-bors-email, r=Kobzol
JonathanBrouwer Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion compiler/rustc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
103 changes: 39 additions & 64 deletions compiler/rustc_abi/src/layout.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -349,8 +348,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
is_enum: bool,
is_special_no_niche: bool,
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
discr_range_of_repr: impl Fn(RangeFrom<i128>, RangeToInclusive<u128>) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, u128)>,
always_sized: bool,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let (present_first, present_second) = {
Expand Down Expand Up @@ -582,8 +581,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
&self,
repr: &ReprOptions,
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
discr_range_of_repr: impl Fn(RangeFrom<i128>, RangeToInclusive<u128>) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, u128)>,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let dl = self.cx.data_layout();
// bail if the enum has an incoherent repr that cannot be computed
Expand Down Expand Up @@ -755,63 +754,36 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
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<i128> = discriminants
let necessary_discriminants: Vec<u128> = 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;
Expand Down Expand Up @@ -929,13 +901,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
}
}

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 };

Expand Down
150 changes: 3 additions & 147 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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};
Expand All @@ -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))]
Expand Down Expand Up @@ -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<NonZero<u16>>` 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<bool, RangeFull> {
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<bool, RangeFull> {
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))]
Expand Down
Loading
Loading