diff options
| author | Jack Wrenn <jack@wrenn.fyi> | 2024-03-19 14:49:13 +0000 | 
|---|---|---|
| committer | Jack Wrenn <jack@wrenn.fyi> | 2024-04-08 15:36:52 +0000 | 
| commit | 3aa14e3b2e61739e8d0ec7883f9c185821ca5da2 (patch) | |
| tree | e9a2c7adb125293cdec06758a2f42aed498bbbff /compiler/rustc_transmute/src | |
| parent | d6eb0f5a09247ff8443e68b4d17e0350c74bafb1 (diff) | |
| download | rust-3aa14e3b2e61739e8d0ec7883f9c185821ca5da2.tar.gz rust-3aa14e3b2e61739e8d0ec7883f9c185821ca5da2.zip | |
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.).
Diffstat (limited to 'compiler/rustc_transmute/src')
| -rw-r--r-- | compiler/rustc_transmute/src/layout/dfa.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/layout/nfa.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/layout/tree.rs | 441 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/lib.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/maybe_transmutable/mod.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/maybe_transmutable/query_context.rs | 10 | 
6 files changed, 230 insertions, 247 deletions
| diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index b8922696e30..77d5a48f158 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -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(); diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs index 78fcceb5f2c..3c5963202c6 100644 --- a/compiler/rustc_transmute/src/layout/nfa.rs +++ b/compiler/rustc_transmute/src/layout/nfa.rs @@ -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) } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index f6bc224c7e7..12c984f1603 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -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)?; - Ok(std::iter::repeat(elt) - .take(len as usize) - .fold(Tree::unit(), |tree, elt| tree.then(elt))) + ty::Uint(nty) => { + let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; + Ok(Self::number(width as _)) } - ty::Adt(adt_def, args_ref) => { - use rustc_middle::ty::AdtKind; + ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx), - // If the layout is ill-specified, halt. - if !(adt_def.repr().c() || adt_def.repr().int.is_some()) { + 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(*count as usize) + .fold(Tree::unit(), |tree, elt| tree.then(elt))) + } - // 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::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), + } } 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; + 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) + } + 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), + } + } + + /// 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); } - // 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(); + 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)) + }, + )?; - // begin with the field's visibility - tree = tree.then(Self::def(Def::Field(field_def))); + return Ok(Self::def(Def::Adt(def)).then(variants)); + } - // compute the field's layout characteristics - let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align); + /// 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); + }; - // 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)); - } + // 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); - // finally, the field's layout - tree = tree.then(Self::from_ty(field_ty, tcx)?); + let mut size = Size::ZERO; + let mut struct_tree = Self::def(def); - // extend the variant layout with the field layout - variant_layout = variant_layout.extend(field_layout).unwrap().0; + // 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)); } - 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) + // 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)) } - pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { + /// 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; - - 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) + /// 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 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)) + } } } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index b6f937ebe47..12312271646 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -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. diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index 16d15580a05..2789fe8f6b1 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -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... diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 54ed03d44e6..1ccb6f36c8e 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -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!() - } } } | 
