Rollup merge of #123367 - jswrenn:layoutify, r=compiler-errors
Safe Transmute: Compute transmutability from `rustc_target::abi::Layout` In its first step of computing transmutability, `rustc_transmutability` constructs a byte-level representation of type layout (`Tree`). Previously, this representation was computed for ADTs by inspecting the ADT definition and performing our own layout computations. This process was error-prone, verbose, and limited our ability to analyze many types (particularly default-repr types). In this PR, we instead construct `Tree`s from `rustc_target::abi::Layout`s. This helps ensure that layout optimizations are reflected our analyses, and increases the kinds of types we can now analyze, including: - default repr ADTs - transparent unions - `UnsafeCell`-containing types Overall, this PR expands the expressvity of `rustc_transmutability` to be much closer to the transmutability analysis performed by miri. Future PRs will work to close the remaining gaps (e.g., support for `Box`, raw pointers, `NonZero*`, coroutines, etc.). r? `@compiler-errors`
This commit is contained in:
commit
0e27c99332
32 changed files with 905 additions and 789 deletions
|
@ -35,6 +35,7 @@ impl<R> Transitions<R>
|
|||
where
|
||||
R: Ref,
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
fn insert(&mut self, transition: Transition<R>, state: State) {
|
||||
match transition {
|
||||
Transition::Byte(b) => {
|
||||
|
@ -82,6 +83,7 @@ impl<R> Dfa<R>
|
|||
where
|
||||
R: Ref,
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn unit() -> Self {
|
||||
let transitions: Map<State, Transitions<R>> = Map::default();
|
||||
let start = State::new();
|
||||
|
|
|
@ -160,6 +160,7 @@ where
|
|||
Self { transitions, start, accepting }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn edges_from(&self, start: State) -> Option<&Map<Transition<R>, Set<State>>> {
|
||||
self.transitions.get(&start)
|
||||
}
|
||||
|
|
|
@ -173,16 +173,20 @@ pub(crate) mod rustc {
|
|||
use super::Tree;
|
||||
use crate::layout::rustc::{Def, Ref};
|
||||
|
||||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use rustc_middle::ty::layout::LayoutCx;
|
||||
use rustc_middle::ty::layout::LayoutError;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::AdtDef;
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::ParamEnv;
|
||||
use rustc_middle::ty::AdtKind;
|
||||
use rustc_middle::ty::List;
|
||||
use rustc_middle::ty::ScalarInt;
|
||||
use rustc_middle::ty::VariantDef;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use rustc_target::abi::Align;
|
||||
use std::alloc;
|
||||
use rustc_target::abi::FieldsShape;
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::abi::TyAndLayout;
|
||||
use rustc_target::abi::Variants;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum Err {
|
||||
|
@ -206,176 +210,64 @@ pub(crate) mod rustc {
|
|||
}
|
||||
}
|
||||
|
||||
trait LayoutExt {
|
||||
fn clamp_align(&self, min_align: Align, max_align: Align) -> Self;
|
||||
}
|
||||
|
||||
impl LayoutExt for alloc::Layout {
|
||||
fn clamp_align(&self, min_align: Align, max_align: Align) -> Self {
|
||||
let min_align = min_align.bytes().try_into().unwrap();
|
||||
let max_align = max_align.bytes().try_into().unwrap();
|
||||
Self::from_size_align(self.size(), self.align().clamp(min_align, max_align)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct LayoutSummary {
|
||||
total_align: Align,
|
||||
total_size: usize,
|
||||
discriminant_size: usize,
|
||||
discriminant_align: Align,
|
||||
}
|
||||
|
||||
impl LayoutSummary {
|
||||
fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
|
||||
use rustc_middle::ty::ParamEnvAnd;
|
||||
use rustc_target::abi::{TyAndLayout, Variants};
|
||||
|
||||
let param_env = ParamEnv::reveal_all();
|
||||
let param_env_and_type = ParamEnvAnd { param_env, value: ty };
|
||||
let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
|
||||
|
||||
let total_size: usize = layout.size().bytes_usize();
|
||||
let total_align: Align = layout.align().abi;
|
||||
let discriminant_align: Align;
|
||||
let discriminant_size: usize;
|
||||
|
||||
if let Variants::Multiple { tag, .. } = layout.variants() {
|
||||
discriminant_align = tag.align(&ctx).abi;
|
||||
discriminant_size = tag.size(&ctx).bytes_usize();
|
||||
} else {
|
||||
discriminant_align = Align::ONE;
|
||||
discriminant_size = 0;
|
||||
};
|
||||
|
||||
Ok(Self { total_align, total_size, discriminant_align, discriminant_size })
|
||||
}
|
||||
|
||||
fn into(&self) -> alloc::Layout {
|
||||
alloc::Layout::from_size_align(
|
||||
self.total_size,
|
||||
self.total_align.bytes().try_into().unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
|
||||
pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
|
||||
use rustc_middle::ty::FloatTy::*;
|
||||
use rustc_middle::ty::IntTy::*;
|
||||
use rustc_middle::ty::UintTy::*;
|
||||
pub fn from_ty(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
use rustc_target::abi::HasDataLayout;
|
||||
|
||||
if let Err(e) = ty.error_reported() {
|
||||
if let Err(e) = ty_and_layout.ty.error_reported() {
|
||||
return Err(Err::TypeError(e));
|
||||
}
|
||||
|
||||
let target = tcx.data_layout();
|
||||
let target = cx.tcx.data_layout();
|
||||
let pointer_size = target.pointer_size;
|
||||
|
||||
match ty.kind() {
|
||||
match ty_and_layout.ty.kind() {
|
||||
ty::Bool => Ok(Self::bool()),
|
||||
|
||||
ty::Int(I8) | ty::Uint(U8) => Ok(Self::u8()),
|
||||
ty::Int(I16) | ty::Uint(U16) => Ok(Self::number(2)),
|
||||
ty::Int(I32) | ty::Uint(U32) | ty::Float(F32) => Ok(Self::number(4)),
|
||||
ty::Int(I64) | ty::Uint(U64) | ty::Float(F64) => Ok(Self::number(8)),
|
||||
ty::Int(I128) | ty::Uint(U128) => Ok(Self::number(16)),
|
||||
ty::Int(Isize) | ty::Uint(Usize) => {
|
||||
Ok(Self::number(target.pointer_size.bytes_usize()))
|
||||
ty::Float(nty) => {
|
||||
let width = nty.bit_width() / 8;
|
||||
Ok(Self::number(width as _))
|
||||
}
|
||||
|
||||
ty::Tuple(members) => {
|
||||
if members.len() == 0 {
|
||||
Ok(Tree::unit())
|
||||
} else {
|
||||
Err(Err::NotYetSupported)
|
||||
}
|
||||
ty::Int(nty) => {
|
||||
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
|
||||
Ok(Self::number(width as _))
|
||||
}
|
||||
|
||||
ty::Array(ty, len) => {
|
||||
let len = len
|
||||
.try_eval_target_usize(tcx, ParamEnv::reveal_all())
|
||||
.ok_or(Err::NotYetSupported)?;
|
||||
let elt = Tree::from_ty(*ty, tcx)?;
|
||||
ty::Uint(nty) => {
|
||||
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
|
||||
Ok(Self::number(width as _))
|
||||
}
|
||||
|
||||
ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx),
|
||||
|
||||
ty::Array(inner_ty, len) => {
|
||||
let FieldsShape::Array { stride, count } = &ty_and_layout.fields else {
|
||||
return Err(Err::NotYetSupported);
|
||||
};
|
||||
let inner_ty_and_layout = cx.layout_of(*inner_ty)?;
|
||||
assert_eq!(*stride, inner_ty_and_layout.size);
|
||||
let elt = Tree::from_ty(inner_ty_and_layout, cx)?;
|
||||
Ok(std::iter::repeat(elt)
|
||||
.take(len as usize)
|
||||
.take(*count as usize)
|
||||
.fold(Tree::unit(), |tree, elt| tree.then(elt)))
|
||||
}
|
||||
|
||||
ty::Adt(adt_def, args_ref) => {
|
||||
use rustc_middle::ty::AdtKind;
|
||||
|
||||
// If the layout is ill-specified, halt.
|
||||
if !(adt_def.repr().c() || adt_def.repr().int.is_some()) {
|
||||
return Err(Err::NotYetSupported);
|
||||
ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => {
|
||||
match adt_def.adt_kind() {
|
||||
AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx),
|
||||
AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx),
|
||||
AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx),
|
||||
}
|
||||
|
||||
// Compute a summary of the type's layout.
|
||||
let layout_summary = LayoutSummary::from_ty(ty, tcx)?;
|
||||
|
||||
// The layout begins with this adt's visibility.
|
||||
let vis = Self::def(Def::Adt(*adt_def));
|
||||
|
||||
// And is followed the layout(s) of its variants
|
||||
Ok(vis.then(match adt_def.adt_kind() {
|
||||
AdtKind::Struct => Self::from_repr_c_variant(
|
||||
ty,
|
||||
*adt_def,
|
||||
args_ref,
|
||||
&layout_summary,
|
||||
None,
|
||||
adt_def.non_enum_variant(),
|
||||
tcx,
|
||||
)?,
|
||||
AdtKind::Enum => {
|
||||
trace!(?adt_def, "treeifying enum");
|
||||
let mut tree = Tree::uninhabited();
|
||||
|
||||
for (idx, variant) in adt_def.variants().iter_enumerated() {
|
||||
let tag = tcx.tag_for_variant((ty, idx));
|
||||
tree = tree.or(Self::from_repr_c_variant(
|
||||
ty,
|
||||
*adt_def,
|
||||
args_ref,
|
||||
&layout_summary,
|
||||
tag,
|
||||
variant,
|
||||
tcx,
|
||||
)?);
|
||||
}
|
||||
|
||||
tree
|
||||
}
|
||||
AdtKind::Union => {
|
||||
// is the layout well-defined?
|
||||
if !adt_def.repr().c() {
|
||||
return Err(Err::NotYetSupported);
|
||||
}
|
||||
|
||||
let ty_layout = layout_of(tcx, ty)?;
|
||||
|
||||
let mut tree = Tree::uninhabited();
|
||||
|
||||
for field in adt_def.all_fields() {
|
||||
let variant_ty = field.ty(tcx, args_ref);
|
||||
let variant_layout = layout_of(tcx, variant_ty)?;
|
||||
let padding_needed = ty_layout.size() - variant_layout.size();
|
||||
let variant = Self::def(Def::Field(field))
|
||||
.then(Self::from_ty(variant_ty, tcx)?)
|
||||
.then(Self::padding(padding_needed));
|
||||
|
||||
tree = tree.or(variant);
|
||||
}
|
||||
|
||||
tree
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
ty::Ref(lifetime, ty, mutability) => {
|
||||
let layout = layout_of(tcx, *ty)?;
|
||||
let align = layout.align();
|
||||
let size = layout.size();
|
||||
let ty_and_layout = cx.layout_of(*ty)?;
|
||||
let align = ty_and_layout.align.abi.bytes() as usize;
|
||||
let size = ty_and_layout.size.bytes_usize();
|
||||
Ok(Tree::Ref(Ref {
|
||||
lifetime: *lifetime,
|
||||
ty: *ty,
|
||||
|
@ -389,80 +281,143 @@ pub(crate) mod rustc {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_repr_c_variant(
|
||||
ty: Ty<'tcx>,
|
||||
adt_def: AdtDef<'tcx>,
|
||||
args_ref: GenericArgsRef<'tcx>,
|
||||
layout_summary: &LayoutSummary,
|
||||
tag: Option<ScalarInt>,
|
||||
variant_def: &'tcx VariantDef,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
/// Constructs a `Tree` from a tuple.
|
||||
fn from_tuple(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
members: &'tcx List<Ty<'tcx>>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
let mut tree = Tree::unit();
|
||||
|
||||
let repr = adt_def.repr();
|
||||
let min_align = repr.align.unwrap_or(Align::ONE);
|
||||
let max_align = repr.pack.unwrap_or(Align::MAX);
|
||||
|
||||
let variant_span = trace_span!(
|
||||
"treeifying variant",
|
||||
min_align = ?min_align,
|
||||
max_align = ?max_align,
|
||||
)
|
||||
.entered();
|
||||
|
||||
let mut variant_layout = alloc::Layout::from_size_align(
|
||||
0,
|
||||
layout_summary.total_align.bytes().try_into().unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The layout of the variant is prefixed by the tag, if any.
|
||||
if let Some(tag) = tag {
|
||||
let tag_layout =
|
||||
alloc::Layout::from_size_align(tag.size().bytes_usize(), 1).unwrap();
|
||||
tree = tree.then(Self::from_tag(tag, tcx));
|
||||
variant_layout = variant_layout.extend(tag_layout).unwrap().0;
|
||||
}
|
||||
|
||||
// Next come fields.
|
||||
let fields_span = trace_span!("treeifying fields").entered();
|
||||
for field_def in variant_def.fields.iter() {
|
||||
let field_ty = field_def.ty(tcx, args_ref);
|
||||
let _span = trace_span!("treeifying field", field = ?field_ty).entered();
|
||||
|
||||
// begin with the field's visibility
|
||||
tree = tree.then(Self::def(Def::Field(field_def)));
|
||||
|
||||
// compute the field's layout characteristics
|
||||
let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align);
|
||||
|
||||
// next comes the field's padding
|
||||
let padding_needed = variant_layout.padding_needed_for(field_layout.align());
|
||||
if padding_needed > 0 {
|
||||
tree = tree.then(Self::padding(padding_needed));
|
||||
match &ty_and_layout.fields {
|
||||
FieldsShape::Primitive => {
|
||||
assert_eq!(members.len(), 1);
|
||||
let inner_ty = members[0];
|
||||
let inner_ty_and_layout = cx.layout_of(inner_ty)?;
|
||||
assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout);
|
||||
Self::from_ty(inner_ty_and_layout, cx)
|
||||
}
|
||||
|
||||
// finally, the field's layout
|
||||
tree = tree.then(Self::from_ty(field_ty, tcx)?);
|
||||
|
||||
// extend the variant layout with the field layout
|
||||
variant_layout = variant_layout.extend(field_layout).unwrap().0;
|
||||
FieldsShape::Arbitrary { offsets, .. } => {
|
||||
assert_eq!(offsets.len(), members.len());
|
||||
Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx)
|
||||
}
|
||||
FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported),
|
||||
}
|
||||
drop(fields_span);
|
||||
|
||||
// finally: padding
|
||||
let padding_span = trace_span!("adding trailing padding").entered();
|
||||
if layout_summary.total_size > variant_layout.size() {
|
||||
let padding_needed = layout_summary.total_size - variant_layout.size();
|
||||
tree = tree.then(Self::padding(padding_needed));
|
||||
};
|
||||
drop(padding_span);
|
||||
drop(variant_span);
|
||||
Ok(tree)
|
||||
}
|
||||
|
||||
pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
|
||||
/// Constructs a `Tree` from a struct.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `def` is not a struct definition.
|
||||
fn from_struct(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
def: AdtDef<'tcx>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
assert!(def.is_struct());
|
||||
let def = Def::Adt(def);
|
||||
Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx)
|
||||
}
|
||||
|
||||
/// Constructs a `Tree` from an enum.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `def` is not an enum definition.
|
||||
fn from_enum(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
def: AdtDef<'tcx>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
assert!(def.is_enum());
|
||||
let layout = ty_and_layout.layout;
|
||||
|
||||
if let Variants::Multiple { tag_field, .. } = layout.variants() {
|
||||
// For enums (but not coroutines), the tag field is
|
||||
// currently always the first field of the layout.
|
||||
assert_eq!(*tag_field, 0);
|
||||
}
|
||||
|
||||
let variants = def.discriminants(cx.tcx()).try_fold(
|
||||
Self::uninhabited(),
|
||||
|variants, (idx, ref discriminant)| {
|
||||
let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx));
|
||||
let variant_def = Def::Variant(def.variant(idx));
|
||||
let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx);
|
||||
let variant = Self::from_variant(
|
||||
variant_def,
|
||||
tag,
|
||||
variant_ty_and_layout,
|
||||
layout.size,
|
||||
cx,
|
||||
)?;
|
||||
Result::<Self, Err>::Ok(variants.or(variant))
|
||||
},
|
||||
)?;
|
||||
|
||||
return Ok(Self::def(Def::Adt(def)).then(variants));
|
||||
}
|
||||
|
||||
/// Constructs a `Tree` from a 'variant-like' layout.
|
||||
///
|
||||
/// A 'variant-like' layout includes those of structs and, of course,
|
||||
/// enum variants. Pragmatically speaking, this method supports anything
|
||||
/// with `FieldsShape::Arbitrary`.
|
||||
///
|
||||
/// Note: This routine assumes that the optional `tag` is the first
|
||||
/// field, and enum callers should check that `tag_field` is, in fact,
|
||||
/// `0`.
|
||||
fn from_variant(
|
||||
def: Def<'tcx>,
|
||||
tag: Option<ScalarInt>,
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
total_size: Size,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
// This constructor does not support non-`FieldsShape::Arbitrary`
|
||||
// layouts.
|
||||
let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields()
|
||||
else {
|
||||
return Err(Err::NotYetSupported);
|
||||
};
|
||||
|
||||
// When this function is invoked with enum variants,
|
||||
// `ty_and_layout.size` does not encompass the entire size of the
|
||||
// enum. We rely on `total_size` for this.
|
||||
assert!(ty_and_layout.size <= total_size);
|
||||
|
||||
let mut size = Size::ZERO;
|
||||
let mut struct_tree = Self::def(def);
|
||||
|
||||
// If a `tag` is provided, place it at the start of the layout.
|
||||
if let Some(tag) = tag {
|
||||
size += tag.size();
|
||||
struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx));
|
||||
}
|
||||
|
||||
// Append the fields, in memory order, to the layout.
|
||||
let inverse_memory_index = memory_index.invert_bijective_mapping();
|
||||
for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() {
|
||||
// Add interfield padding.
|
||||
let padding_needed = offsets[*field_idx] - size;
|
||||
let padding = Self::padding(padding_needed.bytes_usize());
|
||||
|
||||
let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize());
|
||||
let field_tree = Self::from_ty(field_ty_and_layout, cx)?;
|
||||
|
||||
struct_tree = struct_tree.then(padding).then(field_tree);
|
||||
|
||||
size += padding_needed + field_ty_and_layout.size;
|
||||
}
|
||||
|
||||
// Add trailing padding.
|
||||
let padding_needed = total_size - size;
|
||||
let trailing_padding = Self::padding(padding_needed.bytes_usize());
|
||||
|
||||
Ok(struct_tree.then(trailing_padding))
|
||||
}
|
||||
|
||||
/// Constructs a `Tree` representing the value of a enum tag.
|
||||
fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
|
||||
use rustc_target::abi::Endian;
|
||||
let size = tag.size();
|
||||
let bits = tag.to_bits(size).unwrap();
|
||||
|
@ -479,24 +434,42 @@ pub(crate) mod rustc {
|
|||
};
|
||||
Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect())
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_of<'tcx>(
|
||||
ctx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> {
|
||||
use rustc_middle::ty::ParamEnvAnd;
|
||||
use rustc_target::abi::TyAndLayout;
|
||||
/// Constructs a `Tree` from a union.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `def` is not a union definition.
|
||||
fn from_union(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
def: AdtDef<'tcx>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
assert!(def.is_union());
|
||||
|
||||
let param_env = ParamEnv::reveal_all();
|
||||
let param_env_and_type = ParamEnvAnd { param_env, value: ty };
|
||||
let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
|
||||
let layout = alloc::Layout::from_size_align(
|
||||
layout.size().bytes_usize(),
|
||||
layout.align().abi.bytes().try_into().unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
trace!(?ty, ?layout, "computed layout for type");
|
||||
Ok(layout)
|
||||
let union_layout = ty_and_layout.layout;
|
||||
|
||||
// This constructor does not support non-`FieldsShape::Union`
|
||||
// layouts. Fields of this shape are all placed at offset 0.
|
||||
let FieldsShape::Union(fields) = union_layout.fields() else {
|
||||
return Err(Err::NotYetSupported);
|
||||
};
|
||||
|
||||
let fields = &def.non_enum_variant().fields;
|
||||
let fields = fields.iter_enumerated().try_fold(
|
||||
Self::uninhabited(),
|
||||
|fields, (idx, ref field_def)| {
|
||||
let field_def = Def::Field(field_def);
|
||||
let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize());
|
||||
let field = Self::from_ty(field_ty_and_layout, cx)?;
|
||||
let trailing_padding_needed = union_layout.size - field_ty_and_layout.size;
|
||||
let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize());
|
||||
let field_and_padding = field.then(trailing_padding);
|
||||
Result::<Self, Err>::Ok(fields.or(field_and_padding))
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Self::def(Def::Adt(def)).then(fields))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![feature(alloc_layout_extra)]
|
||||
#![feature(never_type)]
|
||||
#![allow(dead_code, unused_variables)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
@ -49,6 +49,8 @@ pub enum Reason<T> {
|
|||
DstIsNotYetSupported,
|
||||
/// The layout of the destination type is bit-incompatible with the source type.
|
||||
DstIsBitIncompatible,
|
||||
/// The destination type is uninhabited.
|
||||
DstUninhabited,
|
||||
/// The destination type may carry safety invariants.
|
||||
DstMayHaveSafetyInvariants,
|
||||
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
|
||||
|
|
|
@ -33,6 +33,9 @@ mod rustc {
|
|||
use super::*;
|
||||
use crate::layout::tree::rustc::Err;
|
||||
|
||||
use rustc_middle::ty::layout::LayoutCx;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::ParamEnv;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
|
@ -43,12 +46,20 @@ mod rustc {
|
|||
pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
|
||||
let Self { src, dst, assume, context } = self;
|
||||
|
||||
let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() };
|
||||
let layout_of = |ty| {
|
||||
layout_cx
|
||||
.layout_of(ty)
|
||||
.map_err(|_| Err::NotYetSupported)
|
||||
.and_then(|tl| Tree::from_ty(tl, layout_cx))
|
||||
};
|
||||
|
||||
// Convert `src` and `dst` from their rustc representations, to `Tree`-based
|
||||
// representations. If these conversions fail, conclude that the transmutation is
|
||||
// unacceptable; the layouts of both the source and destination types must be
|
||||
// well-defined.
|
||||
let src = Tree::from_ty(src, context);
|
||||
let dst = Tree::from_ty(dst, context);
|
||||
let src = layout_of(src);
|
||||
let dst = layout_of(dst);
|
||||
|
||||
match (src, dst) {
|
||||
(Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => {
|
||||
|
@ -86,6 +97,10 @@ where
|
|||
// references.
|
||||
let src = src.prune(&|def| false);
|
||||
|
||||
if src.is_inhabited() && !dst.is_inhabited() {
|
||||
return Answer::No(Reason::DstUninhabited);
|
||||
}
|
||||
|
||||
trace!(?src, "pruned src");
|
||||
|
||||
// Remove all `Def` nodes from `dst`, additionally...
|
||||
|
|
|
@ -5,8 +5,6 @@ pub(crate) trait QueryContext {
|
|||
type Def: layout::Def;
|
||||
type Ref: layout::Ref;
|
||||
type Scope: Copy;
|
||||
|
||||
fn min_align(&self, reference: Self::Ref) -> usize;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -31,10 +29,6 @@ pub(crate) mod test {
|
|||
type Def = Def;
|
||||
type Ref = !;
|
||||
type Scope = ();
|
||||
|
||||
fn min_align(&self, reference: !) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,9 +42,5 @@ mod rustc {
|
|||
type Ref = layout::rustc::Ref<'tcx>;
|
||||
|
||||
type Scope = Ty<'tcx>;
|
||||
|
||||
fn min_align(&self, reference: Self::Ref) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue