diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index e0e9ecaa49c63..9b90a213e3614 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1485,14 +1485,12 @@ impl fmt::Debug for Primitive { } impl Primitive { - pub fn size(self, cx: &C) -> Size { + pub fn size(self, cx: &impl HasDataLayout) -> Size { use Primitive::*; - let dl = cx.data_layout(); - match self { Int(i, _) => i.size(), Float(f) => f.size(), - Pointer(a) => dl.pointer_size_in(a), + Pointer(a) => cx.data_layout().pointer_size_in(a), } } @@ -2049,82 +2047,134 @@ impl Niche { } pub fn available(&self, cx: &C) -> u128 { - let Self { value, valid_range: v, .. } = *self; + let Self { value, valid_range, offset: _ } = *self; let size = value.size(cx); assert!(size.bits() <= 128); - let max_value = size.unsigned_int_max(); // Find out how many values are outside the valid range. - let niche = v.end.wrapping_add(1)..v.start; - niche.end.wrapping_sub(niche.start) & max_value + valid_range.count_unused(size) } + /// Claim `count` currently-unused values from `self.valid_range` to represent + /// additional variants without needing to add an additional tag field. + /// + /// Those must be exactly before or after the existing range. Returns the + /// *first* of those newly claimed values (which is thus either `end + 1` or + /// `start - count`, with appropriate wrapping) along with the updated `Scalar` + /// for the tag field (the same `Primitive` but with an updated range). + /// + /// Returns `None` if that's not possible. + /// + /// Panics if `count == 0`; if you don't need to reserve anything don't call this. pub fn reserve(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> { assert!(count > 0); - let Self { value, valid_range: v, .. } = *self; + let Self { value, valid_range: v, offset: _ } = *self; let size = value.size(cx); assert!(size.bits() <= 128); let max_value = size.unsigned_int_max(); + let signed_max = size.signed_int_max().cast_unsigned(); - let available = v.start.wrapping_sub(v.end).wrapping_sub(1) & max_value; + let available = v.count_unused(size); if count > available { return None; } - // Extend the range of valid values being reserved by moving either `v.start` or `v.end` - // bound. Given an eventual `Option`, we try to maximize the chance for `None` to occupy - // the niche of zero. This is accomplished by preferring enums with 2 variants(`count==1`) - // and always taking the shortest path to niche zero. Having `None` in niche zero can - // enable some special optimizations. - // - // Bound selection criteria: - // 1. Select closest to zero given wrapping semantics. - // 2. Avoid moving past zero if possible. - // - // In practice this means that enums with `count > 1` are unlikely to claim niche zero, - // since they have to fit perfectly. If niche zero is already reserved, the selection of - // bounds are of little interest. - let move_start = |v: WrappingRange| { - let start = v.start.wrapping_sub(count) & max_value; - Some((start, Scalar::Initialized { value, valid_range: v.with_start(start) })) + // These are the two places we can put the new values; the rest of the + // method is dedicated to picking which of these strategies to use. + let move_start = || { + let first_new = v.start.wrapping_sub(count) & max_value; + let valid_range = WrappingRange { start: first_new, ..v }; + Some((first_new, Scalar::Initialized { value, valid_range })) }; - let move_end = |v: WrappingRange| { - let start = v.end.wrapping_add(1) & max_value; + let move_end = || { + let first_new = v.end.wrapping_add(1) & max_value; let end = v.end.wrapping_add(count) & max_value; - Some((start, Scalar::Initialized { value, valid_range: v.with_end(end) })) + let valid_range = WrappingRange { end, ..v }; + Some((first_new, Scalar::Initialized { value, valid_range })) }; - let distance_end_zero = max_value - v.end; - // FIXME: this ought to work for `bool` too, but that seems to be hitting a miscompilation - // - if count == 1 && v != (WrappingRange { start: 0, end: 1 }) { - // We only need one, so just pick the one closest to zero. - // Not only does that obviously use zero if it's possible, but it also - // simplifies testing things like `Option`, since looking for `-1` - // is easier than looking for `1114112` (and matches clang's `WEOF`). - let next_up = size.sign_extend(v.end.wrapping_add(1)).unsigned_abs(); - let next_down = size.sign_extend(v.start.wrapping_sub(1)).unsigned_abs(); - if next_down <= next_up { move_start(v) } else { move_end(v) } - } else if v.start > v.end { - // zero is unavailable because wrapping occurs - move_end(v) - } else if v.start <= distance_end_zero { - if count <= v.start { - move_start(v) + + // If only the *exact* space needed is available, both strategies pick the same place. + // This is common for things like `Option>` or `Option`. + // + // Notably, this currently (2026-08) covers all the *guaranteed* cases, as those all + // have `count == 1` and `available == 1`. (That may change in the future with + // things like size & alignment niches for references, however.) + if count == available { + // While they're the same place, to help make things more readable for humans + // we attempt to pick the approach that will give the canonical full range and + // thus show as `T is ..` in the debug print for the scalar. + // Note that we intentionally do *not* just return `WrappingRange::full` always. + // If it came in as `(..=3) | (5..)`, downstream consumers might be only be handling + // `(..=4) | (5..)` and `(..=3) | (4..)`, not other ways of specifying the same range. + return if v.start == 0 { move_end() } else { move_start() }; + } + + // Our goal when picking whether to put the new values before or after the existing ones + // is to end up with things that will be convenient for codegen later on common platforms. + // + // In particular: + // - We prioritize `None`-like cases, ideally letting it be encoded as zero. + // - If zero is unavailable, we prefer cheap predicates like sign checks. + // - Otherwise we use small values as typically easier to materialize. + + // If zero isn't used already, we want to save it for a `count == 1` case like `None`. + // When reserving multiple niches, we thus try to get *closer* to zero in hopes that + // this type will be wrapped in an Option later and we'll have zero available. + // It's ok to claim zero here, though, if a later niche will still have an efficient + // test via `slt 0` or `sgt 0`. If the current range is `2..=5` and we need 2 more, + // for example, better to return `0..=5` and use `-1` for a later None than to return + // `2..=7` now and end up using `1` for the None later. + if count > 1 && !v.contains(0) { + let space_before_start = v.start; + let slack_before_start = if let Some(left) = space_before_start.checked_sub(count) { + (left > 0 || v.end <= signed_max).then_some(left) } else { - // moved past zero, use other bound - move_end(v) - } - } else { - let end = v.end.wrapping_add(count) & max_value; - let overshot_zero = (1..=v.end).contains(&end); - if overshot_zero { - // moved past zero, use other bound - move_start(v) + None + }; + + let space_after_end = (max_value - v.end).saturating_add(1); + let slack_after_end = if let Some(left) = space_after_end.checked_sub(count) { + (left > 0 || v.start > signed_max).then_some(left) } else { - move_end(v) + None + }; + + if slack_before_start.is_some() || slack_after_end.is_some() { + // Smaller in Option would prefer the None, but in Result it prefers the Ok. + return if slack_before_start.ok_or(()) <= slack_after_end.ok_or(()) { + move_start() + } else { + move_end() + }; } } + + // `0..=0` is a strange niche. We want one more to give `0..=1`, to match `bool`, but + // the default behaviour below for "symmetric around zero" would give `-1..=0` instead. + // Plus `0..=count` just looks nicer than `-count..=0` when seeing the scalar. + if let WrappingRange { start: 0, end: 0 } = v { + return move_end(); + } + + // Pick whichever side is *smaller in magnitude*. + // + // For `count == 1`, that naturally hits the various properties we'd like: + // - if `0` is available, it's obviously the smallest in magnitude. + // - if `0` isn't available, picking something small means + // - We'll pick `-1` for `Option::None`, which is easier to test + // (can just use `slt 0`) than 0x11_0000 and matches Clang's `WEOF`. + // - If we need a constant, a small one is more likely to be doable + // with smaller code size on architectures like x64 which have load + // immediate instructions of different widths. + // + // And if `count > 1` we're out of good options, so this one is tolerable. + let abs_before_start = size.sign_extend(v.start.wrapping_sub(1)).unsigned_abs(); + let abs_after_end = size.sign_extend(v.end.wrapping_add(1)).unsigned_abs(); + // If we have something like `-127..=127` in `i16`, we'd rather pick `-128` + // for the tie to end up at `i8`'s range. (Said otherwise, there are more + // negatives than positives in twos complement, so negative wins in ties.) + if abs_before_start <= abs_after_end { move_start() } else { move_end() } } } diff --git a/compiler/rustc_abi/src/tests.rs b/compiler/rustc_abi/src/tests.rs index d49c2d44af84d..a219782a1bfb1 100644 --- a/compiler/rustc_abi/src/tests.rs +++ b/compiler/rustc_abi/src/tests.rs @@ -68,3 +68,156 @@ fn wrapping_range_contains_range() { assert!(!boolr.contains_range(cmpr, size1)); assert!(cmpr.contains_range(boolr, size1)); } + +#[test] +fn wrapping_range_count_unused() { + let zero = WrappingRange { start: 0, end: 0 }; + assert_eq!(zero.count_unused(Size::from_bytes(1)), u8::MAX.into()); + assert_eq!(zero.count_unused(Size::from_bytes(2)), u16::MAX.into()); + + let full = WrappingRange { start: 2, end: 1 }; + assert_eq!(full.count_unused(Size::from_bytes(1)), 0); + assert_eq!(full.count_unused(Size::from_bytes(2)), 0); + + let byte = WrappingRange::full(Size::from_bytes(1)); + assert_eq!(byte.count_unused(Size::from_bytes(1)), 0); + assert_eq!(byte.count_unused(Size::from_bytes(2)), 0x10000 - 0x100); +} + +fn niche_16(start: u128, end: u128) -> Niche { + Niche { + offset: Size::from_bytes(123), + value: Primitive::Int(Integer::I16, true), + valid_range: WrappingRange { start, end }, + } +} + +#[test] +fn niche_reserve_insufficient_space() { + let n = niche_16(1, u16::MAX.into()); + assert_eq!(n.reserve(&FailCx, 2), None); + + // Callers don't do any pre-checks, so can show up with type-impossible requests too. + // For example, layout asks if it can store 1071 values in a byte for + // `cranelift_assembler_x64::inst::Inst` + let n = niche_16(1, 15); + assert_eq!(n.reserve(&FailCx, u64::MAX.into()), None); +} + +#[test] +fn niche_reserve_full_ranges() { + let full_16 = WrappingRange::full(Size::from_bits(16)); + + let n = niche_16(1, u16::MAX.into()); + let (first, scalar) = n.reserve(&FailCx, 1).unwrap(); + assert_eq!(first, 0); + assert_eq!(scalar.valid_range(&FailCx), full_16); + + let n = niche_16(0, (u16::MAX - 1).into()); + let (first, scalar) = n.reserve(&FailCx, 1).unwrap(); + assert_eq!(first, u16::MAX.into()); + assert_eq!(scalar.valid_range(&FailCx), full_16); + + let n = niche_16(5, 3); + let (first, scalar) = n.reserve(&FailCx, 1).unwrap(); + assert_eq!(first, 4); + // It would also be fine for this to be `start: 5, end: 4`, but this is what we do now. + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 4, end: 3 }); +} + +#[test] +fn niche_reserve_zero_adjacent() { + let n = niche_16(0, 0); + let (first, scalar) = n.reserve(&FailCx, 1).unwrap(); + assert_eq!(first, 1); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 0, end: 1 }); + let (first, scalar) = n.reserve(&FailCx, 11).unwrap(); + assert_eq!(first, 1); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 0, end: 11 }); + + let n = niche_16(0, 0x7FFF); + let (first, scalar) = n.reserve(&FailCx, 1).unwrap(); + assert_eq!(first, 0xFFFF); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 0xFFFF, end: 0x7FFF }); + let (first, scalar) = n.reserve(&FailCx, 16).unwrap(); + assert_eq!(first, 0xFFF0); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 0xFFF0, end: 0x7FFF }); + + let n = niche_16(0x8000, 0); + let (first, scalar) = n.reserve(&FailCx, 1).unwrap(); + assert_eq!(first, 1); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 0x8000, end: 1 }); + let (first, scalar) = n.reserve(&FailCx, 16).unwrap(); + assert_eq!(first, 1); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 0x8000, end: 16 }); +} + +#[test] +fn niche_reserve_multiple_no_wraparound() { + let n = niche_16(10, 1000); + let (first, scalar) = n.reserve(&FailCx, 9).unwrap(); + assert_eq!(first, 1); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 1, end: 1000 }); + let (first, scalar) = n.reserve(&FailCx, 10).unwrap(); + assert_eq!(first, 0); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 0, end: 1000 }); + + let n = niche_16(10, 40_000); + let (first, scalar) = n.reserve(&FailCx, 9).unwrap(); + assert_eq!(first, 1); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 1, end: 40_000 }); + let (first, scalar) = n.reserve(&FailCx, 10).unwrap(); + assert_eq!(first, 40_001); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 10, end: 40_010 }); + + let n = niche_16(40_000, 0xFFFF - 9); + let (first, scalar) = n.reserve(&FailCx, 9).unwrap(); + assert_eq!(first, 0xFFFF - 8); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 40_000, end: 0xFFFF }); + let (first, scalar) = n.reserve(&FailCx, 10).unwrap(); + assert_eq!(first, 0xFFFF - 8); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 40_000, end: 0 }); + + let n = niche_16(10_000, 0xFFFF - 9); + let (first, scalar) = n.reserve(&FailCx, 9).unwrap(); + assert_eq!(first, 0xFFFF - 8); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 10_000, end: 0xFFFF }); + let (first, scalar) = n.reserve(&FailCx, 10).unwrap(); + assert_eq!(first, 9_990); + assert_eq!(scalar.valid_range(&FailCx), WrappingRange { start: 9_990, end: 0xFFFF - 9 }); +} + +#[test] +fn niche_reserve_smaller_magnitude() { + let cases: &[([i16; 2], u128, [i16; 2])] = &[ + ([10, 1000], 1, [9, 1000]), + ([-1000, -10], 1, [-1000, -9]), + ([-2, 3], 1, [-3, 3]), + ([-3, 2], 1, [-3, 3]), + ([-127, 127], 1, [-128, 127]), + ([-1, 1], 1, [-2, 1]), + // Already wrapping, with different counts + ([-2, 1], 1, [-2, 2]), + ([-2, 1], 2, [-2, 3]), + // Multiple with forced wraparound + ([0x0020, -0x0010], 0x22, [0x0020, 0x0012]), + ([0x0010, -0x0020], 0x22, [-0x0012, -0x0020]), + ]; + for &([n_start, n_end], count, [x_start, x_end]) in cases { + let widen = |x: i16| x as u16 as u128; + let n = niche_16(widen(n_start), widen(n_end)); + let (_, scalar) = n.reserve(&FailCx, count).unwrap(); + assert_eq!( + scalar.valid_range(&FailCx), + WrappingRange { start: widen(x_start), end: widen(x_end) }, + "Input niche {n:?}", + ); + } +} + +struct FailCx; +impl HasDataLayout for FailCx { + fn data_layout(&self) -> &TargetDataLayout { + unimplemented!() + } +} diff --git a/compiler/rustc_abi/src/wrapping_range.rs b/compiler/rustc_abi/src/wrapping_range.rs index ecdc7dae88a67..e841b03eff4c6 100644 --- a/compiler/rustc_abi/src/wrapping_range.rs +++ b/compiler/rustc_abi/src/wrapping_range.rs @@ -84,25 +84,23 @@ impl WrappingRange { } } - /// 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`. + /// + /// This is one less than the number of values contained in the range. fn width(&self, size: Size) -> u128 { size.truncate(u128::wrapping_sub(self.end, self.start)) } + /// The count of possible values of this `size` *not* contained in the range. + /// + /// Primarily useful as part of layout calculations to determine the number + /// of additional tags that could be stored in an existing field. + /// + /// Because a `WrappingRange` is never empty, this is strictly less than 2ⁿ. + pub(crate) fn count_unused(&self, size: Size) -> u128 { + size.unsigned_int_max() - self.width(size) + } + /// Returns `true` if `size` completely fills the range. /// /// Note that this is *not* the same as `self == WrappingRange::full(size)`. diff --git a/tests/codegen-llvm/enum/enum-aggregate.rs b/tests/codegen-llvm/enum/enum-aggregate.rs index 46a1a03e96371..6bd4f399b9851 100644 --- a/tests/codegen-llvm/enum/enum-aggregate.rs +++ b/tests/codegen-llvm/enum/enum-aggregate.rs @@ -21,7 +21,7 @@ fn make_some_bool(x: bool) -> Option { fn make_none_bool() -> Option { // CHECK-LABEL: i8 @make_none_bool() // CHECK-NEXT: start: - // CHECK-NEXT: ret i8 2 + // CHECK-NEXT: ret i8 -1 None } diff --git a/tests/codegen-llvm/enum/enum-discriminant-eq.rs b/tests/codegen-llvm/enum/enum-discriminant-eq.rs index 3a006907d78ee..2a7e581b66ed6 100644 --- a/tests/codegen-llvm/enum/enum-discriminant-eq.rs +++ b/tests/codegen-llvm/enum/enum-discriminant-eq.rs @@ -25,8 +25,8 @@ pub enum Giant { #[unsafe(no_mangle)] pub fn opt_bool_eq_discr(a: Option, b: Option) -> bool { // CHECK-LABEL: @opt_bool_eq_discr( - // CHECK: %[[A:.+]] = icmp ne i8 %a, 2 - // CHECK: %[[B:.+]] = icmp eq i8 %b, 2 + // CHECK: %[[A:.+]] = icmp ne i8 %a, -1 + // CHECK: %[[B:.+]] = icmp eq i8 %b, -1 // CHECK: %[[R:.+]] = xor i1 %[[A]], %[[B]] // CHECK: ret i1 %[[R]] @@ -87,16 +87,16 @@ pub enum Mid { pub fn mid_bool_eq_discr(a: Mid, b: Mid) -> bool { // CHECK-LABEL: @mid_bool_eq_discr( - // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, 3 + // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, -2 // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]]) - // CHECK: %[[A_IS_NICHE:.+]] = icmp samesign ugt i8 %a, 1 + // CHECK: %[[A_IS_NICHE:.+]] = icmp slt i8 %a, 0 - // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, 3 + // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, -2 // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]]) - // CHECK: %[[B_IS_NICHE:.+]] = icmp samesign ugt i8 %b, 1 + // CHECK: %[[B_IS_NICHE:.+]] = icmp slt i8 %b, 0 - // CHECK: %[[A_MOD_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %a, i8 3 - // CHECK: %[[B_MOD_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %b, i8 3 + // CHECK: %[[A_MOD_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %a, i8 -2 + // CHECK: %[[B_MOD_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %b, i8 -2 // CHECK: %[[R:.+]] = icmp eq i8 %[[A_MOD_DISCR]], %[[B_MOD_DISCR]] // CHECK: ret i1 %[[R]] @@ -107,16 +107,16 @@ pub fn mid_bool_eq_discr(a: Mid, b: Mid) -> bool { pub fn mid_ord_eq_discr(a: Mid, b: Mid) -> bool { // CHECK-LABEL: @mid_ord_eq_discr( - // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, 3 + // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, -3 // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]]) - // CHECK: %[[A_IS_NICHE:.+]] = icmp sgt i8 %a, 1 + // CHECK: %[[A_IS_NICHE:.+]] = icmp slt i8 %a, -1 - // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, 3 + // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, -3 // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]]) - // CHECK: %[[B_IS_NICHE:.+]] = icmp sgt i8 %b, 1 + // CHECK: %[[B_IS_NICHE:.+]] = icmp slt i8 %b, -1 - // CHECK: %[[A_MOD_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %a, i8 3 - // CHECK: %[[B_MOD_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %b, i8 3 + // CHECK: %[[A_MOD_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %a, i8 -3 + // CHECK: %[[B_MOD_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %b, i8 -3 // CHECK: %[[R:.+]] = icmp eq i8 %[[A_MOD_DISCR]], %[[B_MOD_DISCR]] // CHECK: ret i1 %[[R]] @@ -135,16 +135,16 @@ pub fn mid_nz32_eq_discr(a: Mid>, b: Mid>) -> bool { pub fn mid_ac_eq_discr(a: Mid, b: Mid) -> bool { // CHECK-LABEL: @mid_ac_eq_discr( - // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, -127 + // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, -2 // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]]) // CHECK: %[[A_IS_NICHE:.+]] = icmp slt i8 %a, 0 - // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, -127 + // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, -2 // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]]) // CHECK: %[[B_IS_NICHE:.+]] = icmp slt i8 %b, 0 - // CHECK: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %a, i8 -127 - // CHECK: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %b, i8 -127 + // CHECK: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %a, i8 -2 + // CHECK: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %b, i8 -2 // CHECK: %[[R:.+]] = icmp eq i8 %[[A_DISCR]], %[[B_DISCR]] // CHECK: ret i1 %[[R]] @@ -181,7 +181,7 @@ pub fn mid_giant_eq_discr(a: Mid, b: Mid) -> bool { #[unsafe(no_mangle)] pub fn mid_bool_is_thing(a: Mid) -> bool { // CHECK-LABEL: @mid_bool_is_thing( - // CHECK: %[[R:.+]] = icmp samesign ult i8 %a, 2 + // CHECK: %[[R:.+]] = icmp sgt i8 %a, -1 // CHECK: ret i1 %[[R]] discriminant_value(&a) == 1 } @@ -189,7 +189,7 @@ pub fn mid_bool_is_thing(a: Mid) -> bool { #[unsafe(no_mangle)] pub fn mid_ord_is_thing(a: Mid) -> bool { // CHECK-LABEL: @mid_ord_is_thing( - // CHECK: %[[R:.+]] = icmp slt i8 %a, 2 + // CHECK: %[[R:.+]] = icmp sgt i8 %a, -2 // CHECK: ret i1 %[[R]] discriminant_value(&a) == 1 } diff --git a/tests/codegen-llvm/enum/enum-match.rs b/tests/codegen-llvm/enum/enum-match.rs index a0cba452e123c..edef9fe1c9bdc 100644 --- a/tests/codegen-llvm/enum/enum-match.rs +++ b/tests/codegen-llvm/enum/enum-match.rs @@ -20,7 +20,7 @@ pub enum Enum0 { // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match0(i8{{.+}}%0) // CHECK-NEXT: start: -// CHECK-NEXT: %[[IS_B:.+]] = icmp eq i8 %0, 2 +// CHECK-NEXT: %[[IS_B:.+]] = icmp eq i8 %0, -1 // LLVM22-NEXT: %[[TRUNC:.+]] = and i8 %0, 1 // LLVM22-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %[[TRUNC]] // LLVM23-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %0 @@ -36,19 +36,17 @@ pub fn match0(e: Enum0) -> u8 { // Case 1: Niche values are on a boundary for `range`. pub enum Enum1 { - A(bool), - B, - C, + A(bool), // untagged + B, // tag -2 + C, // tag -1 } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match1(i8{{.+}}%0) // CHECK-NEXT: start: -// CHECK-NEXT: %[[REL_VAR:.+]] = add{{( nsw)?}} i8 %0, -2 -// CHECK-NEXT: %[[REL_VAR_WIDE:.+]] = zext i8 %[[REL_VAR]] to i64 -// CHECK-NEXT: %[[IS_NICHE:.+]] = icmp{{( samesign)?}} ugt i8 %0, 1 -// CHECK-NEXT: %[[NICHE_DISCR:.+]] = add nuw nsw i64 %[[REL_VAR_WIDE]], 1 -// CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i64 %[[NICHE_DISCR]], i64 0 -// CHECK-NEXT: switch i64 %[[DISCR]] +// CHECK-NEXT: %[[IS_NICHE:.+]] = icmp slt i8 %0, 0 +// CHECK-NEXT: %[[NICHE_DISCR:.+]] = add{{( nsw)?}} i8 %0, 3 +// CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i8 %[[NICHE_DISCR]], i8 0 +// CHECK-NEXT: switch i8 %[[DISCR]] #[no_mangle] pub fn match1(e: Enum1) -> u8 { use Enum1::*; @@ -142,20 +140,20 @@ pub fn match3(e: Option<&u8>) -> i16 { #[derive(PartialEq)] pub enum MiddleNiche { - A, // tag 2 - B, // tag 3 + A, // tag -5 + B, // tag -4 C(bool), // untagged - D, // tag 5 - E, // tag 6 + D, // tag -2 + E, // tag -1 } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 -?[0-9]+, -?[0-9]+\))?}} i8 @match4(i8{{.+}}%0) // CHECK-NEXT: start: -// CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %0, 4 +// CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %0, -3 // CHECK-NEXT: call void @llvm.assume(i1 %[[NOT_IMPOSSIBLE]]) -// CHECK-NEXT: %[[REL_VAR:.+]] = add{{( nsw)?}} i8 %0, -2 -// CHECK-NEXT: %[[NOT_NICHE:.+]] = icmp{{( samesign)?}} ult i8 %0, 2 -// CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[NOT_NICHE]], i8 2, i8 %[[REL_VAR]] +// CHECK-NEXT: %[[REL_VAR:.+]] = add{{( nsw)?}} i8 %0, 5 +// CHECK-NEXT: %[[IS_NICHE:.+]] = icmp slt i8 %0, 0 +// CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i8 %[[REL_VAR]], i8 2 // CHECK-NEXT: switch i8 %[[DISCR]] #[no_mangle] pub fn match4(e: MiddleNiche) -> u8 { @@ -171,9 +169,9 @@ pub fn match4(e: MiddleNiche) -> u8 { // CHECK-LABEL: define{{.+}}i1 @match4_is_c(i8{{.+}}%e) // CHECK-NEXT: start -// CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %e, 4 +// CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %e, -3 // CHECK-NEXT: call void @llvm.assume(i1 %[[NOT_IMPOSSIBLE]]) -// CHECK-NEXT: %[[IS_C:.+]] = icmp{{( samesign)?}} ult i8 %e, 2 +// CHECK-NEXT: %[[IS_C:.+]] = icmp sgt i8 %e, -1 // CHECK-NEXT: ret i1 %[[IS_C]] #[no_mangle] pub fn match4_is_c(e: MiddleNiche) -> bool { @@ -447,19 +445,18 @@ pub enum HugeVariantIndex { V255(Never), V256(Never), - Possible257, // tag 2 + Possible257, // tag -3 Bool258(bool), // untagged - Possible259, // tag 4 + Possible259, // tag -1 } // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match5(i8{{.+}}%0) // CHECK-NEXT: start: -// CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %0, 3 +// CHECK-NEXT: %[[NOT_IMPOSSIBLE:.+]] = icmp ne i8 %0, -2 // CHECK-NEXT: call void @llvm.assume(i1 %[[NOT_IMPOSSIBLE]]) -// CHECK-NEXT: %[[REL_VAR:.+]] = add{{( nsw)?}} i8 %0, -2 -// CHECK-NEXT: %[[REL_VAR_WIDE:.+]] = zext i8 %[[REL_VAR]] to i64 -// CHECK-NEXT: %[[IS_NICHE:.+]] = icmp{{( samesign)?}} ugt i8 %0, 1 -// CHECK-NEXT: %[[NICHE_DISCR:.+]] = add nuw nsw i64 %[[REL_VAR_WIDE]], 257 +// CHECK-NEXT: %[[IS_NICHE:.+]] = icmp slt i8 %0, 0 +// CHECK-NEXT: %[[TAG_WIDE:.+]] = sext i8 %0 to i64 +// CHECK-NEXT: %[[NICHE_DISCR:.+]] = add nsw i64 %[[TAG_WIDE]], 260 // CHECK-NEXT: %[[DISCR:.+]] = select i1 %[[IS_NICHE]], i64 %[[NICHE_DISCR]], i64 258 // CHECK-NEXT: switch i64 %[[DISCR]], // CHECK-NEXT: i64 257, diff --git a/tests/codegen-llvm/enum/enum-two-variants-match.rs b/tests/codegen-llvm/enum/enum-two-variants-match.rs index a083bb00422c7..b24cbe9b55070 100644 --- a/tests/codegen-llvm/enum/enum-two-variants-match.rs +++ b/tests/codegen-llvm/enum/enum-two-variants-match.rs @@ -60,7 +60,7 @@ pub fn result_match(x: Result) -> u16 { #[no_mangle] pub fn option_bool_match(x: Option) -> char { // CHECK: %[[RAW:.+]] = load i8, ptr %x - // CHECK: %[[IS_NONE:.+]] = icmp eq i8 %[[RAW]], 2 + // CHECK: %[[IS_NONE:.+]] = icmp eq i8 %[[RAW]], -1 // CHECK: %[[OPT_DISCR:.+]] = select i1 %[[IS_NONE]], i64 0, i64 1 // CHECK: %[[OPT_DISCR_T:.+]] = trunc nuw i64 %[[OPT_DISCR]] to i1 // CHECK: br i1 %[[OPT_DISCR_T]], label %[[BB_SOME:.+]], label %[[BB_NONE:.+]] diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index ef056769b147d..924e2fc6c99ec 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -265,7 +265,7 @@ pub fn return_slice(x: &[u16]) -> &[u16] { x } -// CHECK: { i16, i16 } @enum_id_1(i16 noundef{{( range\(i16 0, 3\))?}} %x.0, i16 %x.1) +// CHECK: { i16, i16 } @enum_id_1(i16 noundef{{( range\(i16 -1, 2\))?}} %x.0, i16 %x.1) #[no_mangle] pub fn enum_id_1(x: Option>) -> Option> { x diff --git a/tests/codegen-llvm/intrinsics/cold_path2.rs b/tests/codegen-llvm/intrinsics/cold_path2.rs index 0891c878fd9c5..a0591fb5b30ed 100644 --- a/tests/codegen-llvm/intrinsics/cold_path2.rs +++ b/tests/codegen-llvm/intrinsics/cold_path2.rs @@ -26,7 +26,7 @@ pub fn test(x: Option) { } // CHECK-LABEL: void @test(i8{{.+}}%x) - // CHECK: %[[IS_NONE:.+]] = icmp eq i8 %x, 2 + // CHECK: %[[IS_NONE:.+]] = icmp eq i8 %x, -1 // CHECK: br i1 %[[IS_NONE]], label %bb2, label %bb1, !prof ![[NUM:[0-9]+]] // CHECK: bb1: // CHECK: path_a diff --git a/tests/codegen-llvm/range-attribute.rs b/tests/codegen-llvm/range-attribute.rs index b057b2386e993..c1e79d1170bde 100644 --- a/tests/codegen-llvm/range-attribute.rs +++ b/tests/codegen-llvm/range-attribute.rs @@ -23,7 +23,7 @@ pub fn nonzero_int(x: NonZero) -> NonZero { x } -// CHECK: noundef range(i8 0, 3) i8 @optional_bool(i8{{.*}} range(i8 0, 3) %x) +// CHECK: noundef range(i8 -1, 2) i8 @optional_bool(i8{{.*}} range(i8 -1, 2) %x) #[no_mangle] pub fn optional_bool(x: Option) -> Option { x @@ -35,7 +35,7 @@ pub enum Enum0 { C, } -// CHECK: noundef range(i8 0, 4) i8 @enum0_value(i8{{.*}} range(i8 0, 4) %x) +// CHECK: noundef range(i8 -2, 2) i8 @enum0_value(i8{{.*}} range(i8 -2, 2) %x) #[no_mangle] pub fn enum0_value(x: Enum0) -> Enum0 { x diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index baaffa7c9de42..91bef983f3bee 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -498,7 +498,7 @@ error: layout_of(Option) = Layout { abi: Align(1 bytes), }, backend_repr: Scalar( - u8 is 0..=2, + u8 is (..=1) | (255..), ), fields: Arbitrary { offsets: [ @@ -512,16 +512,16 @@ error: layout_of(Option) = Layout { Niche { offset: Size(0 bytes), value: u8, - valid_range: 0..=2, + valid_range: (..=1) | (255..), }, ), uninhabited: false, variants: Multiple { - tag: u8 is 0..=2, + tag: u8 is (..=1) | (255..), tag_encoding: Niche { untagged_variant: 1, niche_variants: 0..=0, - niche_start: 2, + niche_start: 255, }, tag_field: 0, variants: [ diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index 4e2e66ba53df4..79f96cf7189a7 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -220,7 +220,7 @@ error: layout_of(NicheFirst) = Layout { abi: Align(1 bytes), }, backend_repr: ScalarPair { - a: u8 is 0..=4, + a: u8 is (..=2) | (254..), b: union u8, b_offset: Size(1 bytes), }, @@ -236,16 +236,16 @@ error: layout_of(NicheFirst) = Layout { Niche { offset: Size(0 bytes), value: u8, - valid_range: 0..=4, + valid_range: (..=2) | (254..), }, ), uninhabited: false, variants: Multiple { - tag: u8 is 0..=4, + tag: u8 is (..=2) | (254..), tag_encoding: Niche { untagged_variant: 0, niche_variants: 1..=2, - niche_start: 3, + niche_start: 254, }, tag_field: 0, variants: [ @@ -310,7 +310,7 @@ error: layout_of(NicheSecond) = Layout { abi: Align(1 bytes), }, backend_repr: ScalarPair { - a: u8 is 0..=4, + a: u8 is (..=2) | (254..), b: union u8, b_offset: Size(1 bytes), }, @@ -326,16 +326,16 @@ error: layout_of(NicheSecond) = Layout { Niche { offset: Size(0 bytes), value: u8, - valid_range: 0..=4, + valid_range: (..=2) | (254..), }, ), uninhabited: false, variants: Multiple { - tag: u8 is 0..=4, + tag: u8 is (..=2) | (254..), tag_encoding: Niche { untagged_variant: 0, niche_variants: 1..=2, - niche_start: 3, + niche_start: 254, }, tag_field: 0, variants: [ diff --git a/tests/ui/layout/nonnull-guaranteed-linux.rs b/tests/ui/layout/nonnull-guaranteed-linux.rs new file mode 100644 index 0000000000000..7cf66640c8d1a --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed-linux.rs @@ -0,0 +1,10 @@ +//@ only-linux +#![feature(rustc_attrs)] +#![crate_type = "lib"] + +// Check that various `#[rustc_nonnull_optimization_guaranteed]` types +// get their expected layouts inside `Option`s. + +#[rustc_dump_layout(backend_repr)] +type OptFd = Option; +//~^ ERROR: Scalar(i32 is ..) diff --git a/tests/ui/layout/nonnull-guaranteed-linux.stderr b/tests/ui/layout/nonnull-guaranteed-linux.stderr new file mode 100644 index 0000000000000..d5c79fd130089 --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed-linux.stderr @@ -0,0 +1,8 @@ +error: backend_repr: Scalar(i32 is ..) + --> $DIR/nonnull-guaranteed-linux.rs:9:1 + | +LL | type OptFd = Option; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/layout/nonnull-guaranteed-ptr.rs b/tests/ui/layout/nonnull-guaranteed-ptr.rs new file mode 100644 index 0000000000000..8290faac417bc --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed-ptr.rs @@ -0,0 +1,20 @@ +//@ only-64bit +#![feature(rustc_attrs)] +#![crate_type = "lib"] + +// Check that various `#[rustc_nonnull_optimization_guaranteed]` types +// get their expected layouts inside `Option`s. + +use std::ptr::NonNull; + +#[rustc_dump_layout(backend_repr)] +type OptNonNull = Option>; +//~^ ERROR: Scalar(pointer is 0..=18446744073709551615) + +#[rustc_dump_layout(backend_repr)] +type OptRef<'a> = Option<&'a String>; +//~^ ERROR: Scalar(pointer is 0..=18446744073709551615) + +#[rustc_dump_layout(backend_repr)] +type OptMut<'a> = Option<&'a mut String>; +//~^ ERROR: Scalar(pointer is 0..=18446744073709551615) diff --git a/tests/ui/layout/nonnull-guaranteed-ptr.stderr b/tests/ui/layout/nonnull-guaranteed-ptr.stderr new file mode 100644 index 0000000000000..de1e687a554cd --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed-ptr.stderr @@ -0,0 +1,20 @@ +error: backend_repr: Scalar(pointer is 0..=18446744073709551615) + --> $DIR/nonnull-guaranteed-ptr.rs:11:1 + | +LL | type OptNonNull = Option>; + | ^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(pointer is 0..=18446744073709551615) + --> $DIR/nonnull-guaranteed-ptr.rs:15:1 + | +LL | type OptRef<'a> = Option<&'a String>; + | ^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(pointer is 0..=18446744073709551615) + --> $DIR/nonnull-guaranteed-ptr.rs:19:1 + | +LL | type OptMut<'a> = Option<&'a mut String>; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/layout/nonnull-guaranteed-windows.rs b/tests/ui/layout/nonnull-guaranteed-windows.rs new file mode 100644 index 0000000000000..1306599155038 --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed-windows.rs @@ -0,0 +1,11 @@ +//@ only-windows +//@ only-64bit +#![feature(rustc_attrs)] +#![crate_type = "lib"] + +// Check that various `#[rustc_nonnull_optimization_guaranteed]` types +// get their expected layouts inside `Option`s. + +#[rustc_dump_layout(backend_repr)] +type OptSocket = Option; +//~^ ERROR: Scalar(u64 is ..) diff --git a/tests/ui/layout/nonnull-guaranteed-windows.stderr b/tests/ui/layout/nonnull-guaranteed-windows.stderr new file mode 100644 index 0000000000000..d2e462bd11729 --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed-windows.stderr @@ -0,0 +1,8 @@ +error: backend_repr: Scalar(u64 is ..) + --> $DIR/nonnull-guaranteed-windows.rs:10:1 + | +LL | type OptSocket = Option; + | ^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/layout/nonnull-guaranteed.rs b/tests/ui/layout/nonnull-guaranteed.rs new file mode 100644 index 0000000000000..a96e99365de07 --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed.rs @@ -0,0 +1,47 @@ +#![feature(rustc_attrs)] +#![crate_type = "lib"] + +// Check that various `#[rustc_nonnull_optimization_guaranteed]` types +// get their expected layouts inside `Option`s. + +use std::num::NonZero; + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroU8 = Option>; +//~^ ERROR: Scalar(u8 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroU16 = Option>; +//~^ ERROR: Scalar(u16 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroU32 = Option>; +//~^ ERROR: Scalar(u32 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroU64 = Option>; +//~^ ERROR: Scalar(u64 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroU128 = Option>; +//~^ ERROR: Scalar(u128 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroI8 = Option>; +//~^ ERROR: Scalar(i8 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroI16 = Option>; +//~^ ERROR: Scalar(i16 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroI32 = Option>; +//~^ ERROR: Scalar(i32 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroI64 = Option>; +//~^ ERROR: Scalar(i64 is ..) + +#[rustc_dump_layout(backend_repr)] +type OptNonZeroI128 = Option>; +//~^ ERROR: Scalar(i128 is ..) diff --git a/tests/ui/layout/nonnull-guaranteed.stderr b/tests/ui/layout/nonnull-guaranteed.stderr new file mode 100644 index 0000000000000..fa6c350d760bd --- /dev/null +++ b/tests/ui/layout/nonnull-guaranteed.stderr @@ -0,0 +1,62 @@ +error: backend_repr: Scalar(u8 is ..) + --> $DIR/nonnull-guaranteed.rs:10:1 + | +LL | type OptNonZeroU8 = Option>; + | ^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(u16 is ..) + --> $DIR/nonnull-guaranteed.rs:14:1 + | +LL | type OptNonZeroU16 = Option>; + | ^^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(u32 is ..) + --> $DIR/nonnull-guaranteed.rs:18:1 + | +LL | type OptNonZeroU32 = Option>; + | ^^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(u64 is ..) + --> $DIR/nonnull-guaranteed.rs:22:1 + | +LL | type OptNonZeroU64 = Option>; + | ^^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(u128 is ..) + --> $DIR/nonnull-guaranteed.rs:26:1 + | +LL | type OptNonZeroU128 = Option>; + | ^^^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(i8 is ..) + --> $DIR/nonnull-guaranteed.rs:30:1 + | +LL | type OptNonZeroI8 = Option>; + | ^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(i16 is ..) + --> $DIR/nonnull-guaranteed.rs:34:1 + | +LL | type OptNonZeroI16 = Option>; + | ^^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(i32 is ..) + --> $DIR/nonnull-guaranteed.rs:38:1 + | +LL | type OptNonZeroI32 = Option>; + | ^^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(i64 is ..) + --> $DIR/nonnull-guaranteed.rs:42:1 + | +LL | type OptNonZeroI64 = Option>; + | ^^^^^^^^^^^^^^^^^^ + +error: backend_repr: Scalar(i128 is ..) + --> $DIR/nonnull-guaranteed.rs:46:1 + | +LL | type OptNonZeroI128 = Option>; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr index 9022151911d37..f659726677fb0 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.stderr +++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr @@ -254,16 +254,16 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { Niche { offset: Size(0 bytes), value: u16, - valid_range: (..=0) | (65535..), + valid_range: 0..=1, }, ), uninhabited: false, variants: Multiple { - tag: u16 is (..=0) | (65535..), + tag: u16 is 0..=1, tag_encoding: Niche { untagged_variant: 1, niche_variants: 0..=0, - niche_start: 65535, + niche_start: 1, }, tag_field: 0, variants: [ diff --git a/tests/ui/mir/enum/convert_non_integer_niche_ok.rs b/tests/ui/mir/enum/convert_non_integer_niche_ok.rs index 24027da54589a..ff7225ebcfc47 100644 --- a/tests/ui/mir/enum/convert_non_integer_niche_ok.rs +++ b/tests/ui/mir/enum/convert_non_integer_niche_ok.rs @@ -17,13 +17,13 @@ enum Nested { #[allow(dead_code)] struct Bar { - a: u16, + a: i16, b: u16, } fn main() { let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 0, b: 0 }) }; let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 1, b: 0 }) }; - let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 2, b: 0 }) }; - let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 3, b: 0 }) }; + let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: -2, b: 0 }) }; + let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: -1, b: 0 }) }; } diff --git a/tests/ui/mir/enum/with_niche_int_break.rs b/tests/ui/mir/enum/with_niche_int_break.rs index d363dc7568a48..f67f12adaa6e0 100644 --- a/tests/ui/mir/enum/with_niche_int_break.rs +++ b/tests/ui/mir/enum/with_niche_int_break.rs @@ -17,5 +17,5 @@ enum Nested { } fn main() { - let _val: Nested = unsafe { std::mem::transmute::(u32::MAX) }; + let _val: Nested = unsafe { std::mem::transmute::(2) }; } diff --git a/tests/ui/mir/enum/with_niche_int_ok.rs b/tests/ui/mir/enum/with_niche_int_ok.rs index 9a3ff3a73beb9..bd77e4bfbbbb9 100644 --- a/tests/ui/mir/enum/with_niche_int_ok.rs +++ b/tests/ui/mir/enum/with_niche_int_ok.rs @@ -16,8 +16,8 @@ enum Nested { } fn main() { - let _val: Nested = unsafe { std::mem::transmute::(0) }; - let _val: Nested = unsafe { std::mem::transmute::(1) }; - let _val: Nested = unsafe { std::mem::transmute::(2) }; - let _val: Nested = unsafe { std::mem::transmute::(3) }; + let _val: Nested = unsafe { std::mem::transmute::(0) }; + let _val: Nested = unsafe { std::mem::transmute::(1) }; + let _val: Nested = unsafe { std::mem::transmute::(-2) }; + let _val: Nested = unsafe { std::mem::transmute::(-1) }; } diff --git a/tests/ui/transmutability/enums/niche_optimization.rs b/tests/ui/transmutability/enums/niche_optimization.rs index 316a857662a20..8c8bde6d88233 100644 --- a/tests/ui/transmutability/enums/niche_optimization.rs +++ b/tests/ui/transmutability/enums/niche_optimization.rs @@ -54,7 +54,7 @@ fn bool() { assert::is_transmutable::(); assert::is_transmutable::(); assert::is_transmutable::(); - assert::is_transmutable::(); + assert::is_transmutable::(); } fn one_niche() {