Skip to content

Commit ced5e04

Browse files
committed
rustc: optimize out uninhabited types and variants.
1 parent f62e43d commit ced5e04

File tree

11 files changed

+197
-92
lines changed

11 files changed

+197
-92
lines changed

src/librustc/ty/layout.rs

Lines changed: 105 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ impl FieldPlacement {
755755
/// in terms of categories of C types there are ABI rules for.
756756
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
757757
pub enum Abi {
758+
Uninhabited,
758759
Scalar(Scalar),
759760
Vector,
760761
Aggregate {
@@ -768,15 +769,15 @@ impl Abi {
768769
/// Returns true if the layout corresponds to an unsized type.
769770
pub fn is_unsized(&self) -> bool {
770771
match *self {
771-
Abi::Scalar(_) | Abi::Vector => false,
772+
Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector => false,
772773
Abi::Aggregate { sized, .. } => !sized
773774
}
774775
}
775776

776777
/// Returns true if the fields of the layout are packed.
777778
pub fn is_packed(&self) -> bool {
778779
match *self {
779-
Abi::Scalar(_) | Abi::Vector => false,
780+
Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector => false,
780781
Abi::Aggregate { packed, .. } => packed
781782
}
782783
}
@@ -807,6 +808,7 @@ pub enum Variants {
807808
/// `Some` is the identity function (with a non-null reference).
808809
NicheFilling {
809810
dataful_variant: usize,
811+
niche_variant: usize,
810812
niche: Scalar,
811813
niche_value: u128,
812814
variants: Vec<CachedLayout>,
@@ -855,6 +857,18 @@ impl CachedLayout {
855857
primitive_align: align
856858
}
857859
}
860+
861+
fn uninhabited(field_count: usize) -> Self {
862+
let align = Align::from_bytes(1, 1).unwrap();
863+
CachedLayout {
864+
variants: Variants::Single { index: 0 },
865+
fields: FieldPlacement::Union(field_count),
866+
abi: Abi::Uninhabited,
867+
align,
868+
primitive_align: align,
869+
size: Size::from_bytes(0)
870+
}
871+
}
858872
}
859873

860874
fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -915,13 +929,14 @@ impl<'a, 'tcx> CachedLayout {
915929
bug!("struct cannot be packed and aligned");
916930
}
917931

918-
let mut align = if packed {
932+
let base_align = if packed {
919933
dl.i8_align
920934
} else {
921935
dl.aggregate_align
922936
};
923937

924-
let mut primitive_align = align;
938+
let mut align = base_align;
939+
let mut primitive_align = base_align;
925940
let mut sized = true;
926941

927942
// Anything with repr(C) or repr(packed) doesn't optimize.
@@ -978,13 +993,17 @@ impl<'a, 'tcx> CachedLayout {
978993
}
979994
}
980995

981-
for i in inverse_memory_index.iter() {
982-
let field = fields[*i as usize];
996+
for &i in &inverse_memory_index {
997+
let field = fields[i as usize];
983998
if !sized {
984999
bug!("univariant: field #{} of `{}` comes after unsized field",
9851000
offsets.len(), ty);
9861001
}
9871002

1003+
if field.abi == Abi::Uninhabited {
1004+
return Ok(CachedLayout::uninhabited(fields.len()));
1005+
}
1006+
9881007
if field.is_unsized() {
9891008
sized = false;
9901009
}
@@ -997,7 +1016,7 @@ impl<'a, 'tcx> CachedLayout {
9971016
}
9981017

9991018
debug!("univariant offset: {:?} field: {:#?}", offset, field);
1000-
offsets[*i as usize] = offset;
1019+
offsets[i as usize] = offset;
10011020

10021021
offset = offset.checked_add(field.size, dl)
10031022
.ok_or(LayoutError::SizeOverflow(ty))?;
@@ -1124,7 +1143,7 @@ impl<'a, 'tcx> CachedLayout {
11241143

11251144
// The never type.
11261145
ty::TyNever => {
1127-
univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?
1146+
tcx.intern_layout(CachedLayout::uninhabited(0))
11281147
}
11291148

11301149
// Potentially-fat pointers.
@@ -1278,11 +1297,15 @@ impl<'a, 'tcx> CachedLayout {
12781297
}).collect::<Result<Vec<_>, _>>()
12791298
}).collect::<Result<Vec<_>, _>>()?;
12801299

1281-
if variants.is_empty() {
1282-
// Uninhabitable; represent as unit
1283-
// (Typechecking will reject discriminant-sizing attrs.)
1284-
1285-
return univariant(&[], &def.repr, StructKind::AlwaysSized);
1300+
let (inh_first, inh_second, inh_third) = {
1301+
let mut inh_variants = (0..variants.len()).filter(|&v| {
1302+
variants[v].iter().all(|f| f.abi != Abi::Uninhabited)
1303+
});
1304+
(inh_variants.next(), inh_variants.next(), inh_variants.next())
1305+
};
1306+
if inh_first.is_none() {
1307+
// Uninhabited because it has no variants, or only uninhabited ones.
1308+
return Ok(tcx.intern_layout(CachedLayout::uninhabited(0)));
12861309
}
12871310

12881311
if def.is_union() {
@@ -1329,49 +1352,58 @@ impl<'a, 'tcx> CachedLayout {
13291352
}));
13301353
}
13311354

1332-
if !def.is_enum() || (variants.len() == 1 &&
1333-
!def.repr.inhibit_enum_layout_opt() &&
1334-
!variants[0].is_empty()) {
1335-
// Struct, or union, or univariant enum equivalent to a struct.
1355+
let is_struct = !def.is_enum() ||
1356+
// Only one variant is inhabited.
1357+
(inh_second.is_none() &&
1358+
// Representation optimizations are allowed.
1359+
!def.repr.inhibit_enum_layout_opt() &&
1360+
// Inhabited variant either has data ...
1361+
(!variants[inh_first.unwrap()].is_empty() ||
1362+
// ... or there other, uninhabited, variants.
1363+
variants.len() > 1));
1364+
if is_struct {
1365+
// Struct, or univariant enum equivalent to a struct.
13361366
// (Typechecking will reject discriminant-sizing attrs.)
13371367

1338-
let kind = if def.is_enum() || variants[0].len() == 0 {
1368+
let v = inh_first.unwrap();
1369+
let kind = if def.is_enum() || variants[v].len() == 0 {
13391370
StructKind::AlwaysSized
13401371
} else {
13411372
let param_env = tcx.param_env(def.did);
1342-
let last_field = def.variants[0].fields.last().unwrap();
1373+
let last_field = def.variants[v].fields.last().unwrap();
13431374
let always_sized = tcx.type_of(last_field.did)
13441375
.is_sized(tcx, param_env, DUMMY_SP);
13451376
if !always_sized { StructKind::MaybeUnsized }
13461377
else { StructKind::AlwaysSized }
13471378
};
13481379

1349-
return univariant(&variants[0], &def.repr, kind);
1380+
let mut st = univariant_uninterned(&variants[v], &def.repr, kind)?;
1381+
st.variants = Variants::Single { index: v };
1382+
return Ok(tcx.intern_layout(st));
13501383
}
13511384

13521385
let no_explicit_discriminants = def.variants.iter().enumerate()
13531386
.all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i));
13541387

1355-
if variants.len() == 2 &&
1388+
if inh_second.is_some() && inh_third.is_none() &&
13561389
!def.repr.inhibit_enum_layout_opt() &&
13571390
no_explicit_discriminants {
13581391
// Nullable pointer optimization
1359-
for i in 0..2 {
1360-
if !variants[1 - i].iter().all(|f| f.is_zst()) {
1392+
let (a, b) = (inh_first.unwrap(), inh_second.unwrap());
1393+
for &(i, other) in &[(a, b), (b, a)] {
1394+
if !variants[other].iter().all(|f| f.is_zst()) {
13611395
continue;
13621396
}
13631397

13641398
for (field_index, field) in variants[i].iter().enumerate() {
13651399
if let Some((offset, niche, niche_value)) = field.find_niche(cx)? {
1366-
let mut st = vec![
1367-
univariant_uninterned(&variants[0],
1368-
&def.repr, StructKind::AlwaysSized)?,
1369-
univariant_uninterned(&variants[1],
1370-
&def.repr, StructKind::AlwaysSized)?
1371-
];
1372-
for (i, v) in st.iter_mut().enumerate() {
1373-
v.variants = Variants::Single { index: i };
1374-
}
1400+
let st = variants.iter().enumerate().map(|(j, v)| {
1401+
let mut st = univariant_uninterned(v,
1402+
&def.repr, StructKind::AlwaysSized)?;
1403+
st.variants = Variants::Single { index: j };
1404+
Ok(st)
1405+
}).collect::<Result<Vec<_>, _>>()?;
1406+
13751407
let offset = st[i].fields.offset(field_index) + offset;
13761408
let CachedLayout {
13771409
size,
@@ -1400,6 +1432,7 @@ impl<'a, 'tcx> CachedLayout {
14001432
return Ok(tcx.intern_layout(CachedLayout {
14011433
variants: Variants::NicheFilling {
14021434
dataful_variant: i,
1435+
niche_variant: other,
14031436
niche,
14041437
niche_value,
14051438
variants: st,
@@ -1419,11 +1452,15 @@ impl<'a, 'tcx> CachedLayout {
14191452
}
14201453

14211454
let (mut min, mut max) = (i128::max_value(), i128::min_value());
1422-
for discr in def.discriminants(tcx) {
1455+
for (i, discr) in def.discriminants(tcx).enumerate() {
1456+
if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) {
1457+
continue;
1458+
}
14231459
let x = discr.to_u128_unchecked() as i128;
14241460
if x < min { min = x; }
14251461
if x > max { max = x; }
14261462
}
1463+
assert!(min <= max, "discriminant range is {}...{}", min, max);
14271464
let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max);
14281465

14291466
let mut align = dl.aggregate_align;
@@ -1498,6 +1535,9 @@ impl<'a, 'tcx> CachedLayout {
14981535
let old_ity_size = min_ity.size();
14991536
let new_ity_size = ity.size();
15001537
for variant in &mut variants {
1538+
if variant.abi == Abi::Uninhabited {
1539+
continue;
1540+
}
15011541
match variant.fields {
15021542
FieldPlacement::Arbitrary { ref mut offsets, .. } => {
15031543
for i in offsets {
@@ -1663,16 +1703,11 @@ impl<'a, 'tcx> CachedLayout {
16631703
};
16641704

16651705
match layout.variants {
1666-
Variants::Single { .. } => {
1667-
let variant_names = || {
1668-
adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::<Vec<_>>()
1669-
};
1670-
debug!("print-type-size `{:#?}` variants: {:?}",
1671-
layout, variant_names());
1672-
assert!(adt_def.variants.len() <= 1,
1673-
"univariant with variants {:?}", variant_names());
1674-
if adt_def.variants.len() == 1 {
1675-
let variant_def = &adt_def.variants[0];
1706+
Variants::Single { index } => {
1707+
debug!("print-type-size `{:#?}` variant {}",
1708+
layout, adt_def.variants[index].name);
1709+
if !adt_def.variants.is_empty() {
1710+
let variant_def = &adt_def.variants[index];
16761711
let fields: Vec<_> =
16771712
variant_def.fields.iter().map(|f| f.name).collect();
16781713
record(adt_kind.into(),
@@ -1697,7 +1732,7 @@ impl<'a, 'tcx> CachedLayout {
16971732
variant_def.fields.iter().map(|f| f.name).collect();
16981733
build_variant_info(Some(variant_def.name),
16991734
&fields,
1700-
layout.for_variant(i))
1735+
layout.for_variant(cx, i))
17011736
})
17021737
.collect();
17031738
record(adt_kind.into(), match layout.variants {
@@ -1989,15 +2024,35 @@ impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for (ty::maps::TyCtxtAt<'a, 'tcx, 'tcx>,
19892024
}
19902025

19912026
impl<'a, 'tcx> TyLayout<'tcx> {
1992-
pub fn for_variant(&self, variant_index: usize) -> Self {
2027+
pub fn for_variant<C>(&self, cx: C, variant_index: usize) -> Self
2028+
where C: LayoutOf<Ty<'tcx>> + HasTyCtxt<'tcx>,
2029+
C::TyLayout: MaybeResult<TyLayout<'tcx>>
2030+
{
19932031
let cached = match self.variants {
1994-
Variants::Single { .. } => self.cached,
2032+
Variants::Single { index } if index == variant_index => self.cached,
2033+
2034+
Variants::Single { index } => {
2035+
// Deny calling for_variant more than once for non-Single enums.
2036+
cx.layout_of(self.ty).map_same(|layout| {
2037+
assert_eq!(layout.variants, Variants::Single { index });
2038+
layout
2039+
});
2040+
2041+
let fields = match self.ty.sty {
2042+
ty::TyAdt(def, _) => def.variants[variant_index].fields.len(),
2043+
_ => bug!()
2044+
};
2045+
let mut cached = CachedLayout::uninhabited(fields);
2046+
cached.variants = Variants::Single { index: variant_index };
2047+
cx.tcx().intern_layout(cached)
2048+
}
19952049

19962050
Variants::NicheFilling { ref variants, .. } |
19972051
Variants::Tagged { ref variants, .. } => {
19982052
&variants[variant_index]
19992053
}
20002054
};
2055+
20012056
assert_eq!(cached.variants, Variants::Single { index: variant_index });
20022057

20032058
TyLayout {
@@ -2138,6 +2193,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
21382193
/// Returns true if the type is a ZST and not unsized.
21392194
pub fn is_zst(&self) -> bool {
21402195
match self.abi {
2196+
Abi::Uninhabited => true,
21412197
Abi::Scalar(_) => false,
21422198
Abi::Vector => self.size.bytes() == 0,
21432199
Abi::Aggregate { sized, .. } => sized && self.size.bytes() == 0
@@ -2241,11 +2297,13 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
22412297
}
22422298
NicheFilling {
22432299
dataful_variant,
2300+
niche_variant,
22442301
ref niche,
22452302
niche_value,
22462303
ref variants,
22472304
} => {
22482305
dataful_variant.hash_stable(hcx, hasher);
2306+
niche_variant.hash_stable(hcx, hasher);
22492307
niche.hash_stable(hcx, hasher);
22502308
niche_value.hash_stable(hcx, hasher);
22512309
variants.hash_stable(hcx, hasher);
@@ -2285,6 +2343,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Abi {
22852343
mem::discriminant(self).hash_stable(hcx, hasher);
22862344

22872345
match *self {
2346+
Uninhabited => {}
22882347
Scalar(ref value) => {
22892348
value.hash_stable(hcx, hasher);
22902349
}

src/librustc_trans/abi.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ pub trait LayoutExt<'tcx> {
278278
impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
279279
fn is_aggregate(&self) -> bool {
280280
match self.abi {
281+
layout::Abi::Uninhabited |
281282
layout::Abi::Scalar(_) |
282283
layout::Abi::Vector => false,
283284
layout::Abi::Aggregate { .. } => true
@@ -286,6 +287,8 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
286287

287288
fn homogeneous_aggregate<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<Reg> {
288289
match self.abi {
290+
layout::Abi::Uninhabited => None,
291+
289292
// The primitive for this algorithm.
290293
layout::Abi::Scalar(ref scalar) => {
291294
let kind = match scalar.value {

src/librustc_trans/cabi_x86_64.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>)
6565
}
6666

6767
match layout.abi {
68+
layout::Abi::Uninhabited => {}
69+
6870
layout::Abi::Scalar(ref scalar) => {
6971
let reg = match scalar.value {
7072
layout::Int(..) |

src/librustc_trans/cabi_x86_win64.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc::ty::layout;
1717
pub fn compute_abi_info(fty: &mut FnType) {
1818
let fixup = |a: &mut ArgType| {
1919
match a.layout.abi {
20+
layout::Abi::Uninhabited => {}
2021
layout::Abi::Aggregate { .. } => {
2122
match a.layout.size.bits() {
2223
8 => a.cast_to(Reg::i8()),

0 commit comments

Comments
 (0)