Skip to content

Commit 9235c9f

Browse files
committed
rustc: leave space for fields of uninhabited types to allow partial initialization.
1 parent 95d0b9e commit 9235c9f

File tree

8 files changed

+124
-46
lines changed

8 files changed

+124
-46
lines changed

src/librustc/ty/layout.rs

+50-23
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,6 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
325325
offsets.len(), ty);
326326
}
327327

328-
if field.abi == Abi::Uninhabited {
329-
return Ok(LayoutDetails::uninhabited(fields.len()));
330-
}
331-
332328
if field.is_unsized() {
333329
sized = false;
334330
}
@@ -451,6 +447,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
451447
}
452448
}
453449

450+
if sized && fields.iter().any(|f| f.abi == Abi::Uninhabited) {
451+
abi = Abi::Uninhabited;
452+
}
453+
454454
Ok(LayoutDetails {
455455
variants: Variants::Single { index: 0 },
456456
fields: FieldPlacement::Arbitrary {
@@ -497,7 +497,13 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
497497

498498
// The never type.
499499
ty::TyNever => {
500-
tcx.intern_layout(LayoutDetails::uninhabited(0))
500+
tcx.intern_layout(LayoutDetails {
501+
variants: Variants::Single { index: 0 },
502+
fields: FieldPlacement::Union(0),
503+
abi: Abi::Uninhabited,
504+
align: dl.i8_align,
505+
size: Size::from_bytes(0)
506+
})
501507
}
502508

503509
// Potentially-fat pointers.
@@ -711,27 +717,37 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
711717
}));
712718
}
713719

714-
let (inh_first, inh_second) = {
715-
let mut inh_variants = (0..variants.len()).filter(|&v| {
716-
variants[v].iter().all(|f| f.abi != Abi::Uninhabited)
720+
// A variant is absent if it's uninhabited and only has ZST fields.
721+
// Present uninhabited variants only require space for their fields,
722+
// but *not* an encoding of the discriminant (e.g. a tag value).
723+
// See issue #49298 for more details on the need to leave space
724+
// for non-ZST uninhabited data (mostly partial initialization).
725+
let absent = |fields: &[TyLayout]| {
726+
let uninhabited = fields.iter().any(|f| f.abi == Abi::Uninhabited);
727+
let is_zst = fields.iter().all(|f| f.is_zst());
728+
uninhabited && is_zst
729+
};
730+
let (present_first, present_second) = {
731+
let mut present_variants = (0..variants.len()).filter(|&v| {
732+
!absent(&variants[v])
717733
});
718-
(inh_variants.next(), inh_variants.next())
734+
(present_variants.next(), present_variants.next())
719735
};
720-
if inh_first.is_none() {
721-
// Uninhabited because it has no variants, or only uninhabited ones.
722-
return Ok(tcx.intern_layout(LayoutDetails::uninhabited(0)));
736+
if present_first.is_none() {
737+
// Uninhabited because it has no variants, or only absent ones.
738+
return tcx.layout_raw(param_env.and(tcx.types.never));
723739
}
724740

725741
let is_struct = !def.is_enum() ||
726-
// Only one variant is inhabited.
727-
(inh_second.is_none() &&
742+
// Only one variant is present.
743+
(present_second.is_none() &&
728744
// Representation optimizations are allowed.
729745
!def.repr.inhibit_enum_layout_opt());
730746
if is_struct {
731747
// Struct, or univariant enum equivalent to a struct.
732748
// (Typechecking will reject discriminant-sizing attrs.)
733749

734-
let v = inh_first.unwrap();
750+
let v = present_first.unwrap();
735751
let kind = if def.is_enum() || variants[v].len() == 0 {
736752
StructKind::AlwaysSized
737753
} else {
@@ -773,7 +789,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
773789

774790
// Find one non-ZST variant.
775791
'variants: for (v, fields) in variants.iter().enumerate() {
776-
if fields.iter().any(|f| f.abi == Abi::Uninhabited) {
792+
if absent(fields) {
777793
continue 'variants;
778794
}
779795
for f in fields {
@@ -816,7 +832,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
816832
let offset = st[i].fields.offset(field_index) + offset;
817833
let size = st[i].size;
818834

819-
let abi = match st[i].abi {
835+
let mut abi = match st[i].abi {
820836
Abi::Scalar(_) => Abi::Scalar(niche.clone()),
821837
Abi::ScalarPair(ref first, ref second) => {
822838
// We need to use scalar_unit to reset the
@@ -833,6 +849,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
833849
_ => Abi::Aggregate { sized: true },
834850
};
835851

852+
if st.iter().all(|v| v.abi == Abi::Uninhabited) {
853+
abi = Abi::Uninhabited;
854+
}
855+
836856
return Ok(tcx.intern_layout(LayoutDetails {
837857
variants: Variants::NicheFilling {
838858
dataful_variant: i,
@@ -959,9 +979,6 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
959979
let old_ity_size = min_ity.size();
960980
let new_ity_size = ity.size();
961981
for variant in &mut layout_variants {
962-
if variant.abi == Abi::Uninhabited {
963-
continue;
964-
}
965982
match variant.fields {
966983
FieldPlacement::Arbitrary { ref mut offsets, .. } => {
967984
for i in offsets {
@@ -1055,6 +1072,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
10551072
}
10561073
}
10571074
}
1075+
1076+
if layout_variants.iter().all(|v| v.abi == Abi::Uninhabited) {
1077+
abi = Abi::Uninhabited;
1078+
}
1079+
10581080
tcx.intern_layout(LayoutDetails {
10591081
variants: Variants::Tagged {
10601082
tag,
@@ -1523,9 +1545,14 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
15231545
ty::TyAdt(def, _) => def.variants[variant_index].fields.len(),
15241546
_ => bug!()
15251547
};
1526-
let mut details = LayoutDetails::uninhabited(fields);
1527-
details.variants = Variants::Single { index: variant_index };
1528-
cx.tcx().intern_layout(details)
1548+
let tcx = cx.tcx();
1549+
tcx.intern_layout(LayoutDetails {
1550+
variants: Variants::Single { index: variant_index },
1551+
fields: FieldPlacement::Union(fields),
1552+
abi: Abi::Uninhabited,
1553+
align: tcx.data_layout.i8_align,
1554+
size: Size::from_bytes(0)
1555+
})
15291556
}
15301557

15311558
Variants::NicheFilling { ref variants, .. } |

src/librustc_target/abi/mod.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -761,17 +761,6 @@ impl LayoutDetails {
761761
align,
762762
}
763763
}
764-
765-
pub fn uninhabited(field_count: usize) -> Self {
766-
let align = Align::from_bytes(1, 1).unwrap();
767-
LayoutDetails {
768-
variants: Variants::Single { index: 0 },
769-
fields: FieldPlacement::Union(field_count),
770-
abi: Abi::Uninhabited,
771-
align,
772-
size: Size::from_bytes(0)
773-
}
774-
}
775764
}
776765

777766
/// The details of the layout of a type, alongside the type itself.
@@ -826,10 +815,10 @@ impl<'a, Ty> TyLayout<'a, Ty> {
826815
/// Returns true if the type is a ZST and not unsized.
827816
pub fn is_zst(&self) -> bool {
828817
match self.abi {
829-
Abi::Uninhabited => true,
830818
Abi::Scalar(_) |
831819
Abi::ScalarPair(..) |
832820
Abi::Vector { .. } => false,
821+
Abi::Uninhabited => self.size.bytes() == 0,
833822
Abi::Aggregate { sized } => sized && self.size.bytes() == 0
834823
}
835824
}

src/librustc_trans/mir/operand.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,9 @@ impl<'a, 'tcx> OperandRef<'tcx> {
223223
let offset = self.layout.fields.offset(i);
224224

225225
let mut val = match (self.val, &self.layout.abi) {
226-
// If we're uninhabited, or the field is ZST, it has no data.
227-
_ if self.layout.abi == layout::Abi::Uninhabited || field.is_zst() => {
228-
return OperandRef {
229-
val: OperandValue::Immediate(C_undef(field.immediate_llvm_type(bx.cx))),
230-
layout: field
231-
};
226+
// If the field is ZST, it has no data.
227+
_ if field.is_zst() => {
228+
return OperandRef::new_zst(bx.cx, field);
232229
}
233230

234231
// Newtype of a scalar, scalar pair or vector.

src/librustc_trans/type_of.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,10 @@ pub trait LayoutLlvmExt<'tcx> {
213213
impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
214214
fn is_llvm_immediate(&self) -> bool {
215215
match self.abi {
216-
layout::Abi::Uninhabited |
217216
layout::Abi::Scalar(_) |
218217
layout::Abi::Vector { .. } => true,
219218
layout::Abi::ScalarPair(..) => false,
219+
layout::Abi::Uninhabited |
220220
layout::Abi::Aggregate { .. } => self.is_zst()
221221
}
222222
}

src/test/run-pass/issue-46845.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ union Foo {
2424
}
2525

2626
// If all the variants are uninhabited, however, the union should be uninhabited.
27+
// NOTE(#49298) the union being uninhabited shouldn't change its size.
2728
union Bar {
2829
_a: (Never, u64),
2930
_b: (u64, Never)
3031
}
3132

3233
fn main() {
3334
assert_eq!(mem::size_of::<Foo>(), 8);
34-
assert_eq!(mem::size_of::<Bar>(), 0);
35+
// See the note on `Bar`'s definition for why this isn't `0`.
36+
assert_eq!(mem::size_of::<Bar>(), 8);
3537

3638
let f = [Foo { a: 42 }, Foo { a: 10 }];
3739
println!("{}", unsafe { f[0].a });

src/test/run-pass/issue-49298.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(test)]
12+
13+
extern crate test;
14+
15+
enum Void {}
16+
17+
fn main() {
18+
let mut x: (Void, usize);
19+
let mut y = 42;
20+
x.1 = 13;
21+
22+
// Make sure `y` stays on the stack.
23+
test::black_box(&mut y);
24+
25+
// Check that the write to `x.1` did not overwrite `y`.
26+
// Note that this doesn't fail with optimizations enabled,
27+
// because we can't keep `x.1` on the stack, like we can `y`,
28+
// as we can't borrow partially initialized variables.
29+
assert_eq!(y.to_string(), "42");
30+
31+
// Check that `(Void, usize)` has space for the `usize` field.
32+
assert_eq!(std::mem::size_of::<(Void, usize)>(),
33+
std::mem::size_of::<usize>());
34+
}

src/test/run-pass/issue-50442.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum Void {}
12+
13+
enum Foo {
14+
A(i32),
15+
B(Void),
16+
C(i32)
17+
}
18+
19+
fn main() {
20+
let _foo = Foo::A(0);
21+
}

src/test/run-pass/type-sizes.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,15 @@ enum EnumSingle5 {
6868
A = 42 as u8,
6969
}
7070

71-
enum NicheFilledEnumWithInhabitedVariant {
71+
enum EnumWithMaybeUninhabitedVariant<T> {
7272
A(&'static ()),
73-
B(&'static (), !),
73+
B(&'static (), T),
74+
C,
75+
}
76+
77+
enum NicheFilledEnumWithAbsentVariant {
78+
A(&'static ()),
79+
B((), !),
7480
C,
7581
}
7682

@@ -107,5 +113,7 @@ pub fn main() {
107113
assert_eq!(size_of::<EnumSingle4>(), 1);
108114
assert_eq!(size_of::<EnumSingle5>(), 1);
109115

110-
assert_eq!(size_of::<NicheFilledEnumWithInhabitedVariant>(), size_of::<&'static ()>());
116+
assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<!>>(),
117+
size_of::<EnumWithMaybeUninhabitedVariant<()>>());
118+
assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>());
111119
}

0 commit comments

Comments
 (0)