Skip to content

Commit 919c409

Browse files
committed
Auto merge of rust-lang#144577 - oli-obk:wrapping-niche, r=scottmcm
Pick the largest niche even if the largest niche is wrapped around fixes rust-lang#144388 r? `@scottmcm`
2 parents c8bb4e8 + 219bad4 commit 919c409

File tree

7 files changed

+342
-33
lines changed

7 files changed

+342
-33
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use std::collections::BTreeSet;
12
use std::fmt::{self, Write};
23
use std::ops::{Bound, Deref};
34
use std::{cmp, iter};
45

56
use rustc_hashes::Hash64;
67
use rustc_index::Idx;
78
use rustc_index::bit_set::BitMatrix;
8-
use tracing::debug;
9+
use tracing::{debug, trace};
910

1011
use crate::{
1112
AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
@@ -766,30 +767,63 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
766767

767768
let niche_filling_layout = calculate_niche_filling_layout();
768769

769-
let (mut min, mut max) = (i128::MAX, i128::MIN);
770770
let discr_type = repr.discr_type();
771-
let bits = Integer::from_attr(dl, discr_type).size().bits();
772-
for (i, mut val) in discriminants {
773-
if !repr.c() && variants[i].iter().any(|f| f.is_uninhabited()) {
774-
continue;
775-
}
776-
if discr_type.is_signed() {
777-
// sign extend the raw representation to be an i128
778-
val = (val << (128 - bits)) >> (128 - bits);
779-
}
780-
if val < min {
781-
min = val;
782-
}
783-
if val > max {
784-
max = val;
785-
}
786-
}
787-
// We might have no inhabited variants, so pretend there's at least one.
788-
if (min, max) == (i128::MAX, i128::MIN) {
789-
min = 0;
790-
max = 0;
791-
}
792-
assert!(min <= max, "discriminant range is {min}...{max}");
771+
let discr_int = Integer::from_attr(dl, discr_type);
772+
// Because we can only represent one range of valid values, we'll look for the
773+
// largest range of invalid values and pick everything else as the range of valid
774+
// values.
775+
776+
// First we need to sort the possible discriminant values so that we can look for the largest gap:
777+
let valid_discriminants: BTreeSet<i128> = discriminants
778+
.filter(|&(i, _)| repr.c() || variants[i].iter().all(|f| !f.is_uninhabited()))
779+
.map(|(_, val)| {
780+
if discr_type.is_signed() {
781+
// sign extend the raw representation to be an i128
782+
// FIXME: do this at the discriminant iterator creation sites
783+
discr_int.size().sign_extend(val as u128)
784+
} else {
785+
val
786+
}
787+
})
788+
.collect();
789+
trace!(?valid_discriminants);
790+
let discriminants = valid_discriminants.iter().copied();
791+
//let next_discriminants = discriminants.clone().cycle().skip(1);
792+
let next_discriminants =
793+
discriminants.clone().chain(valid_discriminants.first().copied()).skip(1);
794+
// Iterate over pairs of each discriminant together with the next one.
795+
// Since they were sorted, we can now compute the niche sizes and pick the largest.
796+
let discriminants = discriminants.zip(next_discriminants);
797+
let largest_niche = discriminants.max_by_key(|&(start, end)| {
798+
trace!(?start, ?end);
799+
// If this is a wraparound range, the niche size is `MAX - abs(diff)`, as the diff between
800+
// the two end points is actually the size of the valid discriminants.
801+
let dist = if start > end {
802+
// Overflow can happen for 128 bit discriminants if `end` is negative.
803+
// But in that case casting to `u128` still gets us the right value,
804+
// as the distance must be positive if the lhs of the subtraction is larger than the rhs.
805+
let dist = start.wrapping_sub(end);
806+
if discr_type.is_signed() {
807+
discr_int.signed_max().wrapping_sub(dist) as u128
808+
} else {
809+
discr_int.size().unsigned_int_max() - dist as u128
810+
}
811+
} else {
812+
// Overflow can happen for 128 bit discriminants if `start` is negative.
813+
// But in that case casting to `u128` still gets us the right value,
814+
// as the distance must be positive if the lhs of the subtraction is larger than the rhs.
815+
end.wrapping_sub(start) as u128
816+
};
817+
trace!(?dist);
818+
dist
819+
});
820+
trace!(?largest_niche);
821+
822+
// `max` is the last valid discriminant before the largest niche
823+
// `min` is the first valid discriminant after the largest niche
824+
let (max, min) = largest_niche
825+
// We might have no inhabited variants, so pretend there's at least one.
826+
.unwrap_or((0, 0));
793827
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
794828

795829
let mut align = dl.aggregate_align;

compiler/rustc_abi/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,19 @@ impl Integer {
12051205
}
12061206
}
12071207

1208+
/// Returns the smallest signed value that can be represented by this Integer.
1209+
#[inline]
1210+
pub fn signed_min(self) -> i128 {
1211+
use Integer::*;
1212+
match self {
1213+
I8 => i8::MIN as i128,
1214+
I16 => i16::MIN as i128,
1215+
I32 => i32::MIN as i128,
1216+
I64 => i64::MIN as i128,
1217+
I128 => i128::MIN,
1218+
}
1219+
}
1220+
12081221
/// Finds the smallest Integer type which can represent the signed value.
12091222
#[inline]
12101223
pub fn fit_signed(x: i128) -> Integer {

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ impl abi::Integer {
107107
abi::Integer::I8
108108
};
109109

110-
// If there are no negative values, we can use the unsigned fit.
111-
if min >= 0 {
110+
// Pick the smallest fit.
111+
if unsigned_fit <= signed_fit {
112112
(cmp::max(unsigned_fit, at_least), false)
113113
} else {
114114
(cmp::max(signed_fit, at_least), true)

compiler/rustc_middle/src/ty/util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use crate::ty::{
3232

3333
#[derive(Copy, Clone, Debug)]
3434
pub struct Discr<'tcx> {
35-
/// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`).
35+
/// Bit representation of the discriminant (e.g., `-1i8` is `0xFF_u128`).
3636
pub val: u128,
3737
pub ty: Ty<'tcx>,
3838
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//! Test that we produce the same niche range no
2+
//! matter of signendess if the discriminants are the same.
3+
4+
#![feature(rustc_attrs)]
5+
6+
#[repr(u16)]
7+
#[rustc_layout(debug)]
8+
enum UnsignedAroundZero {
9+
//~^ ERROR: layout_of
10+
A = 65535,
11+
B = 0,
12+
C = 1,
13+
}
14+
15+
#[repr(i16)]
16+
#[rustc_layout(debug)]
17+
enum SignedAroundZero {
18+
//~^ ERROR: layout_of
19+
A = -1,
20+
B = 0,
21+
C = 1,
22+
}
23+
24+
fn main() {}

0 commit comments

Comments
 (0)