Skip to content

Commit e400158

Browse files
committed
Pick the largest niche even if the largest niche is wrapped around
1 parent 45b0c8d commit e400158

File tree

5 files changed

+70
-35
lines changed

5 files changed

+70
-35
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 37 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,42 @@ 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+
let bits = discr_int.size().bits();
773+
// Because we can only represent one range of valid values, we'll look for the
774+
// largest range of invalid values and pick everything else as the range of valid
775+
// values.
776+
777+
// First we need to sort the possible discriminant values so that we can look for the largest gap:
778+
let valid_discriminants: BTreeSet<i128> = discriminants
779+
.filter(|&(i, _)| repr.c() || variants[i].iter().all(|f| !f.is_uninhabited()))
780+
.map(|(_, val)| {
781+
if discr_type.is_signed() {
782+
// sign extend the raw representation to be an i128
783+
(val << (128 - bits)) >> (128 - bits)
784+
} else {
785+
val
786+
}
787+
})
788+
.collect();
789+
trace!(?valid_discriminants);
790+
let discriminants = valid_discriminants.iter().copied();
791+
let next_discriminants =
792+
discriminants.clone().chain(valid_discriminants.first().copied()).skip(1);
793+
// Iterate over pairs of each discriminant together with the next one.
794+
// Since they were sorted, we can now compute the niche sizes and pick the largest.
795+
let discriminants = discriminants.zip(next_discriminants);
796+
let largest_niche = discriminants.max_by_key(|&(start, end)| {
797+
if start <= end { end - start } else { discr_int.signed_max() - (start - end) }
798+
});
799+
trace!(?largest_niche);
800+
801+
// `max` is the last valid discriminant before the largest niche
802+
// `min` is the first valid discriminant after the largest niche
803+
let (max, min) = largest_niche
804+
// We might have no inhabited variants, so pretend there's at least one.
805+
.unwrap_or((0, 0));
793806
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
794807

795808
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)

tests/ui/enum-discriminant/wrapping_niche.stderr

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ error: layout_of(UnsignedAroundZero) = Layout {
99
I16,
1010
false,
1111
),
12-
valid_range: 0..=65535,
12+
valid_range: (..=1) | (65535..),
1313
},
1414
),
1515
fields: Arbitrary {
@@ -20,15 +20,24 @@ error: layout_of(UnsignedAroundZero) = Layout {
2020
0,
2121
],
2222
},
23-
largest_niche: None,
23+
largest_niche: Some(
24+
Niche {
25+
offset: Size(0 bytes),
26+
value: Int(
27+
I16,
28+
false,
29+
),
30+
valid_range: (..=1) | (65535..),
31+
},
32+
),
2433
uninhabited: false,
2534
variants: Multiple {
2635
tag: Initialized {
2736
value: Int(
2837
I16,
2938
false,
3039
),
31-
valid_range: 0..=65535,
40+
valid_range: (..=1) | (65535..),
3241
},
3342
tag_encoding: Direct,
3443
tag_field: 0,

tests/ui/transmutability/enums/niche_optimization.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ fn one_niche() {
7575

7676
assert::is_transmutable::<OptionLike, u8>();
7777
assert::is_transmutable::<V0, OptionLike>();
78+
assert::is_transmutable::<V1, OptionLike>();
7879
assert::is_transmutable::<V254, OptionLike>();
79-
assert::is_transmutable::<V255, OptionLike>();
8080
}
8181

8282
fn one_niche_alt() {
@@ -97,9 +97,9 @@ fn one_niche_alt() {
9797
};
9898

9999
assert::is_transmutable::<OptionLike, u8>();
100-
assert::is_transmutable::<V0, OptionLike>();
100+
assert::is_transmutable::<V1, OptionLike>();
101+
assert::is_transmutable::<V2, OptionLike>();
101102
assert::is_transmutable::<V254, OptionLike>();
102-
assert::is_transmutable::<V255, OptionLike>();
103103
}
104104

105105
fn two_niche() {
@@ -121,9 +121,9 @@ fn two_niche() {
121121

122122
assert::is_transmutable::<OptionLike, u8>();
123123
assert::is_transmutable::<V0, OptionLike>();
124+
assert::is_transmutable::<V1, OptionLike>();
125+
assert::is_transmutable::<V2, OptionLike>();
124126
assert::is_transmutable::<V253, OptionLike>();
125-
assert::is_transmutable::<V254, OptionLike>();
126-
assert::is_transmutable::<V255, OptionLike>();
127127
}
128128

129129
fn no_niche() {
@@ -142,7 +142,7 @@ fn no_niche() {
142142
}
143143

144144
const _: () = {
145-
assert!(std::mem::size_of::<OptionLike>() == 2);
145+
assert!(std::mem::size_of::<OptionLike>() == 1);
146146
};
147147

148148
#[repr(C)]

0 commit comments

Comments
 (0)