Skip to content

Mark payload fields of ScalarPair enums as Scalar::Union when they're not always initialized #96197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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