Skip to content

Commit

Permalink
Auto merge of #96197 - erikdesjardins:scalarpairenum, r=oli-obk
Browse files Browse the repository at this point in the history
Mark payload fields of ScalarPair enums as Scalar::Union when they're not always initialized

Fixes #96158

r? `@RalfJung`
  • Loading branch information
bors committed Apr 22, 2022
2 parents 8d68f2f + 4dcc1aa commit a8272f2
Show file tree
Hide file tree
Showing 3 changed files with 795 additions and 16 deletions.
37 changes: 21 additions & 16 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,21 +1120,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
match st[i].abi() {
Abi::Scalar(_) => Abi::Scalar(niche_scalar),
Abi::ScalarPair(first, second) => {
// We need to use scalar_unit to reset the
// valid range to the maximal one for that
// primitive, because only the niche is
// guaranteed to be initialised, not the
// other primitive.
// Only the niche is guaranteed to be initialised,
// so use union layout for the other primitive.
if offset.bytes() == 0 {
Abi::ScalarPair(
niche_scalar,
scalar_unit(second.primitive()),
)
Abi::ScalarPair(niche_scalar, second.to_union())
} else {
Abi::ScalarPair(
scalar_unit(first.primitive()),
niche_scalar,
)
Abi::ScalarPair(first.to_union(), niche_scalar)
}
}
_ => Abi::Aggregate { sized: true },
Expand Down Expand Up @@ -1329,22 +1320,30 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
} else {
// Try to use a ScalarPair for all tagged enums.
let mut common_prim = None;
let mut common_prim_initialized_in_all_variants = true;
for (field_layouts, layout_variant) in iter::zip(&variants, &layout_variants) {
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
bug!();
};
let mut fields =
iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
let (field, offset) = match (fields.next(), fields.next()) {
(None, None) => continue,
(None, None) => {
common_prim_initialized_in_all_variants = false;
continue;
}
(Some(pair), None) => pair,
_ => {
common_prim = None;
break;
}
};
let prim = match field.abi {
Abi::Scalar(scalar) => scalar.primitive(),
Abi::Scalar(scalar) => {
common_prim_initialized_in_all_variants &=
matches!(scalar, Scalar::Initialized { .. });
scalar.primitive()
}
_ => {
common_prim = None;
break;
Expand All @@ -1364,7 +1363,13 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
}
}
if let Some((prim, offset)) = common_prim {
let pair = self.scalar_pair(tag, scalar_unit(prim));
let prim_scalar = if common_prim_initialized_in_all_variants {
scalar_unit(prim)
} else {
// Common prim might be uninit.
Scalar::Union { value: prim }
};
let pair = self.scalar_pair(tag, prim_scalar);
let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index, &[0, 1]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN"
#![crate_type = "lib"]
#![feature(rustc_attrs)]

use std::mem::MaybeUninit;

enum HasNiche {
A,
B,
C,
}

// This should result in ScalarPair(Initialized, Union),
// since the u8 payload will be uninit for `None`.
#[rustc_layout(debug)]
pub enum MissingPayloadField { //~ ERROR: layout_of
Some(u8),
None
}

// This should result in ScalarPair(Initialized, Initialized),
// since the u8 field is present in all variants,
// and hence will always be initialized.
#[rustc_layout(debug)]
pub enum CommonPayloadField { //~ ERROR: layout_of
A(u8),
B(u8),
}

// This should result in ScalarPair(Initialized, Union),
// since, though a u8-sized field is present in all variants, it might be uninit.
#[rustc_layout(debug)]
pub enum CommonPayloadFieldIsMaybeUninit { //~ ERROR: layout_of
A(u8),
B(MaybeUninit<u8>),
}

// This should result in ScalarPair(Initialized, Union),
// since only the niche field (used for the tag) is guaranteed to be initialized.
#[rustc_layout(debug)]
pub enum NicheFirst { //~ ERROR: layout_of
A(HasNiche, u8),
B,
C
}

// This should result in ScalarPair(Union, Initialized),
// since only the niche field (used for the tag) is guaranteed to be initialized.
#[rustc_layout(debug)]
pub enum NicheSecond { //~ ERROR: layout_of
A(u8, HasNiche),
B,
C,
}
Loading

0 comments on commit a8272f2

Please sign in to comment.