diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index fc5be11114401..0c88bd3dcbc54 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2558,32 +2558,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { rustc_transmute::Reason::SrcIsNotYetSupported => { format!("analyzing the transmutability of `{src}` is not yet supported") } - rustc_transmute::Reason::DstIsNotYetSupported => { format!("analyzing the transmutability of `{dst}` is not yet supported") } - rustc_transmute::Reason::DstIsBitIncompatible => { format!( "at least one value of `{src}` isn't a bit-valid value of `{dst}`" ) } - rustc_transmute::Reason::DstUninhabited => { format!("`{dst}` is uninhabited") } - rustc_transmute::Reason::DstMayHaveSafetyInvariants => { format!("`{dst}` may carry safety invariants") } rustc_transmute::Reason::DstIsTooBig => { format!("the size of `{src}` is smaller than the size of `{dst}`") } - rustc_transmute::Reason::DstRefIsTooBig { src, dst } => { - let src_size = src.size; - let dst_size = dst.size; + rustc_transmute::Reason::DstRefIsTooBig { + src, + src_size, + dst, + dst_size, + } => { format!( - "the referent size of `{src}` ({src_size} bytes) \ + "the size of `{src}` ({src_size} bytes) \ is smaller than that of `{dst}` ({dst_size} bytes)" ) } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 7acf0f990d1b3..786afd7cf48c3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -9,13 +9,12 @@ use std::ops::ControlFlow; -use rustc_ast::Mutability; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast, elaborate}; +use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, Upcast, elaborate}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use thin_vec::thin_vec; @@ -286,99 +285,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result, SelectionError<'tcx>> { use rustc_transmute::{Answer, Assume, Condition}; - /// Generate sub-obligations for reference-to-reference transmutations. - fn reference_obligations<'tcx>( - tcx: TyCtxt<'tcx>, - obligation: &PolyTraitObligation<'tcx>, - (src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability), - (dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability), - assume: Assume, - ) -> PredicateObligations<'tcx> { - let make_transmute_obl = |src, dst| { - let transmute_trait = obligation.predicate.def_id(); - let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2); - let trait_ref = ty::TraitRef::new( - tcx, - transmute_trait, - [ - ty::GenericArg::from(dst), - ty::GenericArg::from(src), - ty::GenericArg::from(assume), - ], - ); - Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - ) - }; - - let make_freeze_obl = |ty| { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Freeze, obligation.cause.span), - [ty::GenericArg::from(ty)], - ); - Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - ) - }; - - let make_outlives_obl = |target, region| { - let outlives = ty::OutlivesPredicate(target, region); - Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - outlives, - ) - }; - - // Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`, - // it is always the case that `Src` must be transmutable into `Dst`, - // and that that `'src` must outlive `'dst`. - let mut obls = PredicateObligations::with_capacity(1); - obls.push(make_transmute_obl(src_ty, dst_ty)); - if !assume.lifetimes { - obls.push(make_outlives_obl(src_lifetime, dst_lifetime)); - } - - // Given a transmutation from `&Src`, both `Src` and `Dst` must be - // `Freeze`, otherwise, using the transmuted value could lead to - // data races. - if src_mut == Mutability::Not { - obls.extend([make_freeze_obl(src_ty), make_freeze_obl(dst_ty)]) - } - - // Given a transmutation into `&'dst mut Dst`, it also must be the - // case that `Dst` is transmutable into `Src`. For example, - // transmuting bool -> u8 is OK as long as you can't update that u8 - // to be > 1, because you could later transmute the u8 back to a - // bool and get undefined behavior. It also must be the case that - // `'dst` lives exactly as long as `'src`. - if dst_mut == Mutability::Mut { - obls.push(make_transmute_obl(dst_ty, src_ty)); - if !assume.lifetimes { - obls.push(make_outlives_obl(dst_lifetime, src_lifetime)); - } - } - - obls - } - /// Flatten the `Condition` tree into a conjunction of obligations. #[instrument(level = "debug", skip(tcx, obligation))] fn flatten_answer_tree<'tcx>( tcx: TyCtxt<'tcx>, obligation: &PolyTraitObligation<'tcx>, - cond: Condition>, + cond: Condition, Ty<'tcx>>, assume: Assume, ) -> PredicateObligations<'tcx> { match cond { @@ -388,13 +300,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .into_iter() .flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume)) .collect(), - Condition::IfTransmutable { src, dst } => reference_obligations( - tcx, - obligation, - (src.lifetime, src.ty, src.mutability), - (dst.lifetime, dst.ty, dst.mutability), - assume, - ), + Condition::Immutable { ty } => { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Freeze, obligation.cause.span), + [ty::GenericArg::from(ty)], + ); + thin_vec![Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + trait_ref, + )] + } + Condition::Outlives { long, short } => { + let outlives = ty::OutlivesPredicate(long, short); + thin_vec![Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + outlives, + )] + } + Condition::Transmutable { src, dst } => { + let transmute_trait = obligation.predicate.def_id(); + let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2); + let trait_ref = ty::TraitRef::new( + tcx, + transmute_trait, + [ + ty::GenericArg::from(dst), + ty::GenericArg::from(src), + ty::GenericArg::from(assume), + ], + ); + thin_vec![Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + trait_ref, + )] + } } } diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index 6d072c336af23..6fc40ce42d88c 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -2,32 +2,35 @@ use std::fmt; use std::iter::Peekable; use std::sync::atomic::{AtomicU32, Ordering}; -use super::{Byte, Ref, Tree, Uninhabited}; +use super::{Byte, Reference, Region, Tree, Type, Uninhabited}; use crate::{Map, Set}; #[derive(PartialEq)] #[cfg_attr(test, derive(Clone))] -pub(crate) struct Dfa +pub(crate) struct Dfa where - R: Ref, + R: Region, + T: Type, { - pub(crate) transitions: Map>, + pub(crate) transitions: Map>, pub(crate) start: State, pub(crate) accept: State, } #[derive(PartialEq, Clone, Debug)] -pub(crate) struct Transitions +pub(crate) struct Transitions where - R: Ref, + R: Region, + T: Type, { byte_transitions: EdgeSet, - ref_transitions: Map, + ref_transitions: Map, State>, } -impl Default for Transitions +impl Default for Transitions where - R: Ref, + R: Region, + T: Type, { fn default() -> Self { Self { byte_transitions: EdgeSet::empty(), ref_transitions: Map::default() } @@ -51,9 +54,10 @@ impl fmt::Debug for State { } } -impl Dfa +impl Dfa where - R: Ref, + R: Region, + T: Type, { #[cfg(test)] pub(crate) fn bool() -> Self { @@ -64,7 +68,7 @@ where } pub(crate) fn unit() -> Self { - let transitions: Map> = Map::default(); + let transitions: Map> = Map::default(); let start = State::new(); let accept = start; @@ -78,21 +82,21 @@ where }) } - pub(crate) fn from_ref(r: R) -> Self { + pub(crate) fn from_ref(r: Reference) -> Self { Self::from_transitions(|accept| Transitions { byte_transitions: EdgeSet::empty(), ref_transitions: [(r, accept)].into_iter().collect(), }) } - fn from_transitions(f: impl FnOnce(State) -> Transitions) -> Self { + fn from_transitions(f: impl FnOnce(State) -> Transitions) -> Self { let start = State::new(); let accept = State::new(); Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept } } - pub(crate) fn from_tree(tree: Tree) -> Result { + pub(crate) fn from_tree(tree: Tree) -> Result { Ok(match tree { Tree::Byte(b) => Self::from_byte(b), Tree::Ref(r) => Self::from_ref(r), @@ -125,7 +129,7 @@ where let start = self.start; let accept = other.accept; - let mut transitions: Map> = self.transitions; + let mut transitions: Map> = self.transitions; for (source, transition) in other.transitions { let fix_state = |state| if state == other.start { self.accept } else { state }; @@ -169,7 +173,7 @@ where }; let start = mapped((Some(a.start), Some(b.start))); - let mut transitions: Map> = Map::default(); + let mut transitions: Map> = Map::default(); let empty_transitions = Transitions::default(); struct WorkQueue { @@ -257,7 +261,7 @@ where .flat_map(|transitions| transitions.byte_transitions.iter()) } - pub(crate) fn refs_from(&self, start: State) -> impl Iterator { + pub(crate) fn refs_from(&self, start: State) -> impl Iterator, State)> { self.transitions .get(&start) .into_iter() @@ -297,9 +301,10 @@ where } /// Serialize the DFA using the Graphviz DOT format. -impl fmt::Debug for Dfa +impl fmt::Debug for Dfa where - R: Ref, + R: Region, + T: Type, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "digraph {{")?; diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index c08bf440734e2..acbce258f39be 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -78,16 +78,41 @@ impl From for Byte { } } +/// A reference, i.e., `&'region T` or `&'region mut T`. +#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +pub(crate) struct Reference +where + R: Region, + T: Type, +{ + pub(crate) region: R, + pub(crate) is_mut: bool, + pub(crate) referent: T, + pub(crate) referent_size: usize, + pub(crate) referent_align: usize, +} + +impl fmt::Display for Reference +where + R: Region, + T: Type, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("&")?; + if self.is_mut { + f.write_str("mut ")?; + } + self.referent.fmt(f) + } +} + pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone { fn has_safety_invariants(&self) -> bool; } -pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone { - fn min_align(&self) -> usize; - fn size(&self) -> usize; +pub(crate) trait Region: Debug + Hash + Eq + PartialEq + Copy + Clone {} - fn is_mutable(&self) -> bool; -} +pub(crate) trait Type: Debug + Hash + Eq + PartialEq + Copy + Clone {} impl Def for ! { fn has_safety_invariants(&self) -> bool { @@ -95,79 +120,21 @@ impl Def for ! { } } -impl Ref for ! { - fn min_align(&self) -> usize { - unreachable!() - } - fn size(&self) -> usize { - unreachable!() - } - fn is_mutable(&self) -> bool { - unreachable!() - } -} +impl Region for ! {} -#[cfg(test)] -impl Ref for [(); N] { - fn min_align(&self) -> usize { - N - } +impl Type for ! {} - fn size(&self) -> usize { - N - } +#[cfg(test)] +impl Region for usize {} - fn is_mutable(&self) -> bool { - false - } -} +#[cfg(test)] +impl Type for () {} #[cfg(feature = "rustc")] pub mod rustc { - use std::fmt::{self, Write}; - use rustc_abi::Layout; - use rustc_middle::mir::Mutability; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError}; - use rustc_middle::ty::{self, Ty}; - - /// A reference in the layout. - #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] - pub struct Ref<'tcx> { - pub lifetime: ty::Region<'tcx>, - pub ty: Ty<'tcx>, - pub mutability: Mutability, - pub align: usize, - pub size: usize, - } - - impl<'tcx> super::Ref for Ref<'tcx> { - fn min_align(&self) -> usize { - self.align - } - - fn size(&self) -> usize { - self.size - } - - fn is_mutable(&self) -> bool { - match self.mutability { - Mutability::Mut => true, - Mutability::Not => false, - } - } - } - impl<'tcx> Ref<'tcx> {} - - impl<'tcx> fmt::Display for Ref<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_char('&')?; - if self.mutability == Mutability::Mut { - f.write_str("mut ")?; - } - self.ty.fmt(f) - } - } + use rustc_middle::ty::{self, Region, Ty}; /// A visibility node in the layout. #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] @@ -187,6 +154,10 @@ pub mod rustc { } } + impl<'tcx> super::Region for Region<'tcx> {} + + impl<'tcx> super::Type for Ty<'tcx> {} + pub(crate) fn layout_of<'tcx>( cx: LayoutCx<'tcx>, ty: Ty<'tcx>, diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index ff665695b5a36..372b4f5353abb 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -1,6 +1,6 @@ use std::ops::{ControlFlow, RangeInclusive}; -use super::{Byte, Def, Ref}; +use super::{Byte, Def, Reference, Region, Type}; #[cfg(test)] mod tests; @@ -15,10 +15,11 @@ mod tests; /// 2. A `Seq` is never directly nested beneath another `Seq`. /// 3. `Seq`s and `Alt`s with a single member do not exist. #[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub(crate) enum Tree +pub(crate) enum Tree where D: Def, - R: Ref, + R: Region, + T: Type, { /// A sequence of successive layouts. Seq(Vec), @@ -27,7 +28,7 @@ where /// A definition node. Def(D), /// A reference node. - Ref(R), + Ref(Reference), /// A byte node. Byte(Byte), } @@ -48,10 +49,11 @@ impl From for Endian { } } -impl Tree +impl Tree where D: Def, - R: Ref, + R: Region, + T: Type, { /// A `Tree` consisting only of a definition node. pub(crate) fn def(def: D) -> Self { @@ -138,7 +140,7 @@ where /// Remove all `Def` nodes, and all branches of the layout for which `f` /// produces `true`. - pub(crate) fn prune(self, f: &F) -> Tree + pub(crate) fn prune(self, f: &F) -> Tree where F: Fn(D) -> bool, { @@ -198,13 +200,13 @@ where /// Produces a `Tree` where each of the trees in `trees` are sequenced one /// after another. - pub(crate) fn seq(trees: [Tree; N]) -> Self { + pub(crate) fn seq(trees: [Tree; N]) -> Self { trees.into_iter().fold(Tree::unit(), Self::then) } /// Produces a `Tree` where each of the trees in `trees` are accepted as /// alternative layouts. - pub(crate) fn alt(trees: [Tree; N]) -> Self { + pub(crate) fn alt(trees: [Tree; N]) -> Self { trees.into_iter().fold(Tree::uninhabited(), Self::or) } @@ -251,11 +253,14 @@ pub(crate) mod rustc { FieldIdx, FieldsShape, Layout, Size, TagEncoding, TyAndLayout, VariantIdx, Variants, }; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError}; - use rustc_middle::ty::{self, AdtDef, AdtKind, List, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; + use rustc_middle::ty::{ + self, AdtDef, AdtKind, List, Region, ScalarInt, Ty, TyCtxt, TypeVisitableExt, + }; use rustc_span::ErrorGuaranteed; use super::Tree; - use crate::layout::rustc::{Def, Ref, layout_of}; + use crate::layout::Reference; + use crate::layout::rustc::{Def, layout_of}; #[derive(Debug, Copy, Clone)] pub(crate) enum Err { @@ -281,7 +286,7 @@ pub(crate) mod rustc { } } - impl<'tcx> Tree, Ref<'tcx>> { + impl<'tcx> Tree, Region<'tcx>, Ty<'tcx>> { pub(crate) fn from_ty(ty: Ty<'tcx>, cx: LayoutCx<'tcx>) -> Result { use rustc_abi::HasDataLayout; let layout = layout_of(cx, ty)?; @@ -353,16 +358,17 @@ pub(crate) mod rustc { } } - ty::Ref(lifetime, ty, mutability) => { + ty::Ref(region, ty, mutability) => { let layout = layout_of(cx, *ty)?; - let align = layout.align.abi.bytes_usize(); - let size = layout.size.bytes_usize(); - Ok(Tree::Ref(Ref { - lifetime: *lifetime, - ty: *ty, - mutability: *mutability, - align, - size, + let referent_align = layout.align.abi.bytes_usize(); + let referent_size = layout.size.bytes_usize(); + + Ok(Tree::Ref(Reference { + region: *region, + is_mut: mutability.is_mut(), + referent: *ty, + referent_align, + referent_size, })) } diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs index 8c3dbbe37ab21..bc47b19c681f4 100644 --- a/compiler/rustc_transmute/src/layout/tree/tests.rs +++ b/compiler/rustc_transmute/src/layout/tree/tests.rs @@ -20,13 +20,13 @@ mod prune { #[test] fn seq_1() { - let layout: Tree = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00)); + let layout: Tree = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00)); assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } #[test] fn seq_2() { - let layout: Tree = + let layout: Tree = Tree::byte(0x00).then(Tree::def(Def::NoSafetyInvariants)).then(Tree::byte(0x01)); assert_eq!( @@ -41,7 +41,7 @@ mod prune { #[test] fn invisible_def() { - let layout: Tree = Tree::def(Def::HasSafetyInvariants); + let layout: Tree = Tree::def(Def::HasSafetyInvariants); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::uninhabited() @@ -50,7 +50,7 @@ mod prune { #[test] fn invisible_def_in_seq_len_2() { - let layout: Tree = + let layout: Tree = Tree::def(Def::NoSafetyInvariants).then(Tree::def(Def::HasSafetyInvariants)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), @@ -60,7 +60,7 @@ mod prune { #[test] fn invisible_def_in_seq_len_3() { - let layout: Tree = Tree::def(Def::NoSafetyInvariants) + let layout: Tree = Tree::def(Def::NoSafetyInvariants) .then(Tree::byte(0x00)) .then(Tree::def(Def::HasSafetyInvariants)); assert_eq!( @@ -75,20 +75,20 @@ mod prune { #[test] fn visible_def() { - let layout: Tree = Tree::def(Def::NoSafetyInvariants); + let layout: Tree = Tree::def(Def::NoSafetyInvariants); assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::unit()); } #[test] fn visible_def_in_seq_len_2() { - let layout: Tree = + let layout: Tree = Tree::def(Def::NoSafetyInvariants).then(Tree::def(Def::NoSafetyInvariants)); assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::unit()); } #[test] fn visible_def_in_seq_len_3() { - let layout: Tree = Tree::def(Def::NoSafetyInvariants) + let layout: Tree = Tree::def(Def::NoSafetyInvariants) .then(Tree::byte(0x00)) .then(Tree::def(Def::NoSafetyInvariants)); assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index ce18dad55179c..36281ff16bceb 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -19,23 +19,29 @@ pub struct Assume { /// Either transmutation is allowed, we have an error, or we have an optional /// Condition that must hold. #[derive(Debug, Hash, Eq, PartialEq, Clone)] -pub enum Answer { +pub enum Answer { Yes, - No(Reason), - If(Condition), + No(Reason), + If(Condition), } /// A condition which must hold for safe transmutation to be possible. #[derive(Debug, Hash, Eq, PartialEq, Clone)] -pub enum Condition { +pub enum Condition { /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`. - IfTransmutable { src: R, dst: R }, + Transmutable { src: T, dst: T }, + + /// The region `long` must outlive `short`. + Outlives { long: R, short: R }, + + /// The `ty` is immutable. + Immutable { ty: T }, /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met. - IfAll(Vec>), + IfAll(Vec>), /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met. - IfAny(Vec>), + IfAny(Vec>), } /// Answers "why wasn't the source type transmutable into the destination type?" @@ -53,12 +59,16 @@ pub enum Reason { DstMayHaveSafetyInvariants, /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. DstIsTooBig, - /// A referent of `Dst` is larger than a referent in `Src`. + /// `Dst` is larger `Src`. DstRefIsTooBig { /// The referent of the source type. src: T, + /// The size of the source type's referent. + src_size: usize, /// The too-large referent of the destination type. dst: T, + /// The size of the destination type's referent. + dst_size: usize, }, /// Src should have a stricter alignment than Dst, but it does not. DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize }, @@ -79,7 +89,7 @@ pub enum Reason { #[cfg(feature = "rustc")] mod rustc { use rustc_hir::lang_items::LangItem; - use rustc_middle::ty::{Const, Ty, TyCtxt}; + use rustc_middle::ty::{Const, Region, Ty, TyCtxt}; use super::*; @@ -105,7 +115,7 @@ mod rustc { &mut self, types: Types<'tcx>, assume: crate::Assume, - ) -> crate::Answer> { + ) -> crate::Answer, Ty<'tcx>> { crate::maybe_transmutable::MaybeTransmutableQuery::new( types.src, types.dst, assume, self.tcx, ) diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index f76abe50ed343..062de4b5d08a7 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -5,7 +5,7 @@ pub(crate) mod query_context; #[cfg(test)] mod tests; -use crate::layout::{self, Def, Dfa, Ref, Tree, dfa, union}; +use crate::layout::{self, Def, Dfa, Reference, Tree, dfa, union}; use crate::maybe_transmutable::query_context::QueryContext; use crate::{Answer, Condition, Map, Reason}; @@ -41,7 +41,10 @@ mod rustc { /// This method begins by converting `src` and `dst` from `Ty`s to `Tree`s, /// then computes an answer using those trees. #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] - pub(crate) fn answer(self) -> Answer< as QueryContext>::Ref> { + pub(crate) fn answer( + self, + ) -> Answer< as QueryContext>::Region, as QueryContext>::Type> + { let Self { src, dst, assume, context } = self; let layout_cx = LayoutCx::new(context, TypingEnv::fully_monomorphized()); @@ -67,7 +70,11 @@ mod rustc { } } -impl MaybeTransmutableQuery::Def, ::Ref>, C> +impl + MaybeTransmutableQuery< + Tree<::Def, ::Region, ::Type>, + C, + > where C: QueryContext, { @@ -77,7 +84,7 @@ where /// then converts `src` and `dst` to `Dfa`s, and computes an answer using those DFAs. #[inline(always)] #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] - pub(crate) fn answer(self) -> Answer<::Ref> { + pub(crate) fn answer(self) -> Answer<::Region, ::Type> { let Self { src, dst, assume, context } = self; // Unconditionally remove all `Def` nodes from `src`, without pruning away the @@ -130,12 +137,12 @@ where } } -impl MaybeTransmutableQuery::Ref>, C> +impl MaybeTransmutableQuery::Region, ::Type>, C> where C: QueryContext, { /// Answers whether a `Dfa` is transmutable into another `Dfa`. - pub(crate) fn answer(self) -> Answer<::Ref> { + pub(crate) fn answer(self) -> Answer<::Region, ::Type> { self.answer_memo(&mut Map::default(), self.src.start, self.dst.start) } @@ -143,10 +150,13 @@ where #[instrument(level = "debug", skip(self))] fn answer_memo( &self, - cache: &mut Map<(dfa::State, dfa::State), Answer<::Ref>>, + cache: &mut Map< + (dfa::State, dfa::State), + Answer<::Region, ::Type>, + >, src_state: dfa::State, dst_state: dfa::State, - ) -> Answer<::Ref> { + ) -> Answer<::Region, ::Type> { if let Some(answer) = cache.get(&(src_state, dst_state)) { answer.clone() } else { @@ -160,10 +170,13 @@ where fn answer_impl( &self, - cache: &mut Map<(dfa::State, dfa::State), Answer<::Ref>>, + cache: &mut Map< + (dfa::State, dfa::State), + Answer<::Region, ::Type>, + >, src_state: dfa::State, dst_state: dfa::State, - ) -> Answer<::Ref> { + ) -> Answer<::Region, ::Type> { debug!(?src_state, ?dst_state); debug!(src = ?self.src); debug!(dst = ?self.dst); @@ -247,27 +260,51 @@ where // ...there exists a reference transition out of `dst_state`... Quantifier::ThereExists.apply(self.dst.refs_from(dst_state).map( |(dst_ref, dst_state_prime)| { - if !src_ref.is_mutable() && dst_ref.is_mutable() { + if !src_ref.is_mut && dst_ref.is_mut { Answer::No(Reason::DstIsMoreUnique) } else if !self.assume.alignment - && src_ref.min_align() < dst_ref.min_align() + && src_ref.referent_align < dst_ref.referent_align { Answer::No(Reason::DstHasStricterAlignment { - src_min_align: src_ref.min_align(), - dst_min_align: dst_ref.min_align(), + src_min_align: src_ref.referent_align, + dst_min_align: dst_ref.referent_align, + }) + } else if dst_ref.referent_size > src_ref.referent_size { + Answer::No(Reason::DstRefIsTooBig { + src: src_ref.referent, + src_size: src_ref.referent_size, + dst: dst_ref.referent, + dst_size: dst_ref.referent_size, }) - } else if dst_ref.size() > src_ref.size() { - Answer::No(Reason::DstRefIsTooBig { src: src_ref, dst: dst_ref }) } else { - // ...such that `src` is transmutable into `dst`, if - // `src_ref` is transmutability into `dst_ref`. - and( - Answer::If(Condition::IfTransmutable { - src: src_ref, - dst: dst_ref, - }), - self.answer_memo(cache, src_state_prime, dst_state_prime), - ) + let mut conditions = Vec::with_capacity(4); + let mut is_transmutable = + |src: Reference<_, _>, dst: Reference<_, _>| { + conditions.push(Condition::Transmutable { + src: src.referent, + dst: dst.referent, + }); + if !self.assume.lifetimes { + conditions.push(Condition::Outlives { + long: src.region, + short: dst.region, + }); + } + }; + + is_transmutable(src_ref, dst_ref); + + if dst_ref.is_mut { + is_transmutable(dst_ref, src_ref); + } else { + conditions.push(Condition::Immutable { ty: dst_ref.referent }); + } + + Answer::If(Condition::IfAll(conditions)).and(self.answer_memo( + cache, + src_state_prime, + dst_state_prime, + )) } }, )) @@ -275,67 +312,65 @@ where ); if self.assume.validity { - or(bytes_answer, refs_answer) + bytes_answer.or(refs_answer) } else { - and(bytes_answer, refs_answer) + bytes_answer.and(refs_answer) } } } } -fn and(lhs: Answer, rhs: Answer) -> Answer -where - R: PartialEq, -{ - match (lhs, rhs) { - // If both are errors, then we should return the more specific one - (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) - | (Answer::No(reason), Answer::No(_)) - // If either is an error, return it - | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason), - // If only one side has a condition, pass it along - | (Answer::Yes, other) | (other, Answer::Yes) => other, - // If both sides have IfAll conditions, merge them - (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => { - lhs.append(rhs); - Answer::If(Condition::IfAll(lhs)) - } - // If only one side is an IfAll, add the other Condition to it - (Answer::If(cond), Answer::If(Condition::IfAll(mut conds))) - | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => { - conds.push(cond); - Answer::If(Condition::IfAll(conds)) +impl Answer { + fn and(self, rhs: Answer) -> Answer { + let lhs = self; + match (lhs, rhs) { + // If both are errors, then we should return the more specific one + (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) + | (Answer::No(reason), Answer::No(_)) + // If either is an error, return it + | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason), + // If only one side has a condition, pass it along + | (Answer::Yes, other) | (other, Answer::Yes) => other, + // If both sides have IfAll conditions, merge them + (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => { + lhs.append(rhs); + Answer::If(Condition::IfAll(lhs)) + } + // If only one side is an IfAll, add the other Condition to it + (Answer::If(cond), Answer::If(Condition::IfAll(mut conds))) + | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => { + conds.push(cond); + Answer::If(Condition::IfAll(conds)) + } + // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll + (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])), } - // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll - (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])), } -} -fn or(lhs: Answer, rhs: Answer) -> Answer -where - R: PartialEq, -{ - match (lhs, rhs) { - // If both are errors, then we should return the more specific one - (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) - | (Answer::No(reason), Answer::No(_)) => Answer::No(reason), - // Otherwise, errors can be ignored for the rest of the pattern matching - (Answer::No(_), other) | (other, Answer::No(_)) => or(other, Answer::Yes), - // If only one side has a condition, pass it along - (Answer::Yes, other) | (other, Answer::Yes) => other, - // If both sides have IfAny conditions, merge them - (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => { - lhs.append(rhs); - Answer::If(Condition::IfAny(lhs)) - } - // If only one side is an IfAny, add the other Condition to it - (Answer::If(cond), Answer::If(Condition::IfAny(mut conds))) - | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => { - conds.push(cond); - Answer::If(Condition::IfAny(conds)) + fn or(self, rhs: Answer) -> Answer { + let lhs = self; + match (lhs, rhs) { + // If both are errors, then we should return the more specific one + (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) + | (Answer::No(reason), Answer::No(_)) => Answer::No(reason), + // Otherwise, errors can be ignored for the rest of the pattern matching + (Answer::No(_), other) | (other, Answer::No(_)) => other.or(Answer::Yes), + // If only one side has a condition, pass it along + (Answer::Yes, other) | (other, Answer::Yes) => other, + // If both sides have IfAny conditions, merge them + (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => { + lhs.append(rhs); + Answer::If(Condition::IfAny(lhs)) + } + // If only one side is an IfAny, add the other Condition to it + (Answer::If(cond), Answer::If(Condition::IfAny(mut conds))) + | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => { + conds.push(cond); + Answer::If(Condition::IfAny(conds)) + } + // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny + (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])), } - // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny - (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])), } } @@ -345,24 +380,25 @@ enum Quantifier { } impl Quantifier { - fn apply(&self, iter: I) -> Answer + fn apply(&self, iter: I) -> Answer where - R: layout::Ref, - I: IntoIterator>, + R: layout::Region, + T: layout::Type, + I: IntoIterator>, { use std::ops::ControlFlow::{Break, Continue}; let (init, try_fold_f): (_, fn(_, _) -> _) = match self { Self::ThereExists => { - (Answer::No(Reason::DstIsBitIncompatible), |accum: Answer, next| { - match or(accum, next) { - Answer::Yes => Break(Answer::Yes), - maybe => Continue(maybe), - } + (Answer::No(Reason::DstIsBitIncompatible), |accum: Answer, next| match accum + .or(next) + { + Answer::Yes => Break(Answer::Yes), + maybe => Continue(maybe), }) } - Self::ForAll => (Answer::Yes, |accum: Answer, next| { - let answer = and(accum, next); + Self::ForAll => (Answer::Yes, |accum: Answer, next| { + let answer = accum.and(next); match answer { Answer::No(_) => Break(answer), maybe => Continue(maybe), diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 214da101be375..6be0cb1190436 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -3,7 +3,8 @@ use crate::layout; /// Context necessary to answer the question "Are these types transmutable?". pub(crate) trait QueryContext { type Def: layout::Def; - type Ref: layout::Ref; + type Region: layout::Region; + type Type: layout::Type; } #[cfg(test)] @@ -12,9 +13,9 @@ pub(crate) mod test { use super::QueryContext; - pub(crate) struct UltraMinimal(PhantomData); + pub(crate) struct UltraMinimal(PhantomData<(R, T)>); - impl Default for UltraMinimal { + impl Default for UltraMinimal { fn default() -> Self { Self(PhantomData) } @@ -32,20 +33,26 @@ pub(crate) mod test { } } - impl QueryContext for UltraMinimal { + impl QueryContext for UltraMinimal + where + R: crate::layout::Region, + T: crate::layout::Type, + { type Def = Def; - type Ref = R; + type Region = R; + type Type = T; } } #[cfg(feature = "rustc")] mod rustc { - use rustc_middle::ty::TyCtxt; + use rustc_middle::ty::{Region, Ty, TyCtxt}; use super::*; impl<'tcx> super::QueryContext for TyCtxt<'tcx> { type Def = layout::rustc::Def<'tcx>; - type Ref = layout::rustc::Ref<'tcx>; + type Region = Region<'tcx>; + type Type = Ty<'tcx>; } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 0227ad71ae660..8440ace260883 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -3,17 +3,17 @@ extern crate test; use itertools::Itertools; use super::query_context::test::{Def, UltraMinimal}; -use crate::{Answer, Assume, Reason, layout}; +use crate::{Answer, Assume, Condition, Reason, layout}; -type Tree = layout::Tree; -type Dfa = layout::Dfa; +type Tree = layout::Tree; +type Dfa = layout::Dfa; trait Representation { - fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer; + fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer; } impl Representation for Tree { - fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { + fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { crate::maybe_transmutable::MaybeTransmutableQuery::new( src, dst, @@ -25,7 +25,7 @@ impl Representation for Tree { } impl Representation for Dfa { - fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { + fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { crate::maybe_transmutable::MaybeTransmutableQuery::new( src, dst, @@ -40,7 +40,7 @@ fn is_transmutable( src: &R, dst: &R, assume: Assume, -) -> crate::Answer { +) -> crate::Answer { let src = src.clone(); let dst = dst.clone(); // The only dimension of the transmutability analysis we want to test @@ -53,7 +53,7 @@ mod safety { use super::*; use crate::Answer; - const DST_HAS_SAFETY_INVARIANTS: Answer = + const DST_HAS_SAFETY_INVARIANTS: Answer = Answer::No(crate::Reason::DstMayHaveSafetyInvariants); #[test] @@ -177,14 +177,15 @@ mod bool { #[test] fn should_permit_validity_expansion_and_reject_contraction() { - let b0 = layout::Tree::::byte(0); - let b1 = layout::Tree::::byte(1); - let b2 = layout::Tree::::byte(2); + let b0 = layout::Tree::::byte(0); + let b1 = layout::Tree::::byte(1); + let b2 = layout::Tree::::byte(2); let alts = [b0, b1, b2]; let into_layout = |alts: Vec<_>| { - alts.into_iter().fold(layout::Tree::::uninhabited(), layout::Tree::::or) + alts.into_iter() + .fold(layout::Tree::::uninhabited(), layout::Tree::::or) }; let into_set = |alts: Vec<_>| { @@ -277,7 +278,7 @@ mod alt { #[test] fn should_permit_identity_transmutation() { - type Tree = layout::Tree; + type Tree = layout::Tree; let x = Tree::Seq(vec![Tree::byte(0), Tree::byte(0)]); let y = Tree::Seq(vec![Tree::bool(), Tree::byte(1)]); @@ -331,7 +332,7 @@ mod char { fn should_permit_valid_transmutation() { for order in [Endian::Big, Endian::Little] { use Answer::*; - let char_layout = layout::Tree::::char(order); + let char_layout = layout::Tree::::char(order); // `char`s can be in the following ranges: // - [0, 0xD7FF] @@ -353,7 +354,7 @@ mod char { (0xFFFFFFFF, no), ] { let src_layout = - layout::tree::Tree::::from_big_endian(order, src.to_be_bytes()); + layout::tree::Tree::::from_big_endian(order, src.to_be_bytes()); let a = is_transmutable(&src_layout, &char_layout, Assume::default()); assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}"); @@ -371,7 +372,7 @@ mod nonzero { #[test] fn should_permit_identity_transmutation() { for width in NONZERO_BYTE_WIDTHS { - let layout = layout::Tree::::nonzero(width); + let layout = layout::Tree::::nonzero(width); assert_eq!(is_transmutable(&layout, &layout, Assume::default()), Answer::Yes); } } @@ -381,8 +382,8 @@ mod nonzero { for width in NONZERO_BYTE_WIDTHS { use Answer::*; - let num = layout::Tree::::number(width); - let nz = layout::Tree::::nonzero(width); + let num = layout::Tree::::number(width); + let nz = layout::Tree::::nonzero(width); let a = is_transmutable(&num, &nz, Assume::default()); assert_eq!(a, No(Reason::DstIsBitIncompatible), "width:{width}"); @@ -395,13 +396,23 @@ mod nonzero { mod r#ref { use super::*; + use crate::layout::Reference; #[test] fn should_permit_identity_transmutation() { - type Tree = crate::layout::Tree; + type Tree = crate::layout::Tree; for validity in [false, true] { - let layout = Tree::Seq(vec![Tree::byte(0x00), Tree::Ref([()])]); + let layout = Tree::Seq(vec![ + Tree::byte(0x00), + Tree::Ref(Reference { + region: 42, + is_mut: false, + referent: (), + referent_size: 0, + referent_align: 1, + }), + ]); let assume = Assume { validity, ..Assume::default() }; @@ -414,7 +425,11 @@ mod r#ref { .answer(); assert_eq!( answer, - Answer::If(crate::Condition::IfTransmutable { src: [()], dst: [()] }) + Answer::If(Condition::IfAll(vec![ + Condition::Transmutable { src: (), dst: () }, + Condition::Outlives { long: 42, short: 42 }, + Condition::Immutable { ty: () }, + ])) ); } } diff --git a/tests/ui/transmutability/references/reject_extension.stderr b/tests/ui/transmutability/references/reject_extension.stderr index 182106acf12cf..a5f677850946d 100644 --- a/tests/ui/transmutability/references/reject_extension.stderr +++ b/tests/ui/transmutability/references/reject_extension.stderr @@ -2,7 +2,7 @@ error[E0277]: `&Packed` cannot be safely transmuted into `&Packed` --> $DIR/reject_extension.rs:48:37 | LL | assert::is_transmutable::<&Src, &Dst>(); - | ^^^^ the referent size of `&Packed` (2 bytes) is smaller than that of `&Packed` (4 bytes) + | ^^^^ the size of `Packed` (2 bytes) is smaller than that of `Packed` (4 bytes) | note: required by a bound in `is_transmutable` --> $DIR/reject_extension.rs:13:14 diff --git a/tests/ui/transmutability/references/unit-to-u8.stderr b/tests/ui/transmutability/references/unit-to-u8.stderr index bc9f286e097a9..35d1179f451b1 100644 --- a/tests/ui/transmutability/references/unit-to-u8.stderr +++ b/tests/ui/transmutability/references/unit-to-u8.stderr @@ -2,7 +2,7 @@ error[E0277]: `&Unit` cannot be safely transmuted into `&u8` --> $DIR/unit-to-u8.rs:22:52 | LL | assert::is_maybe_transmutable::<&'static Unit, &'static u8>(); - | ^^^^^^^^^^^ the referent size of `&Unit` (0 bytes) is smaller than that of `&u8` (1 bytes) + | ^^^^^^^^^^^ the size of `Unit` (0 bytes) is smaller than that of `u8` (1 bytes) | note: required by a bound in `is_maybe_transmutable` --> $DIR/unit-to-u8.rs:9:14 diff --git a/tests/ui/transmutability/references/unsafecell.rs b/tests/ui/transmutability/references/unsafecell.rs index 4001f139770a5..b0db4af45f54b 100644 --- a/tests/ui/transmutability/references/unsafecell.rs +++ b/tests/ui/transmutability/references/unsafecell.rs @@ -38,10 +38,10 @@ fn mut_to_mut() { } fn mut_to_ref() { - // We don't care about `UnsafeCell` for transmutations in the form `&mut T - // -> &U`, because downgrading a `&mut T` to a `&U` deactivates `&mut T` for - // the lifetime of `&U`. - assert::is_maybe_transmutable::<&'static mut u8, &'static UnsafeCell>(); - assert::is_maybe_transmutable::<&'static mut UnsafeCell, &'static u8>(); - assert::is_maybe_transmutable::<&'static mut UnsafeCell, &'static UnsafeCell>(); + // `&mut UnsafeCell` is irrelevant in the source. + assert::is_maybe_transmutable::<&'static mut UnsafeCell, &'static u8>(); + // `&UnsafeCell` in forbidden in the destination, since the destination can be used to + // invalidate a shadowed source reference. + assert::is_maybe_transmutable::<&'static mut bool, &'static UnsafeCell>(); //~ ERROR: cannot be safely transmuted + assert::is_maybe_transmutable::<&'static mut UnsafeCell, &'static UnsafeCell>(); //~ ERROR: cannot be safely transmuted } diff --git a/tests/ui/transmutability/references/unsafecell.stderr b/tests/ui/transmutability/references/unsafecell.stderr index 6664d8a7d6fbc..02a0935e84eae 100644 --- a/tests/ui/transmutability/references/unsafecell.stderr +++ b/tests/ui/transmutability/references/unsafecell.stderr @@ -28,6 +28,36 @@ LL | where LL | Dst: TransmuteFrom | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` -error: aborting due to 2 previous errors +error[E0277]: `&mut bool` cannot be safely transmuted into `&UnsafeCell` + --> $DIR/unsafecell.rs:45:56 + | +LL | assert::is_maybe_transmutable::<&'static mut bool, &'static UnsafeCell>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `UnsafeCell` + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/unsafecell.rs:12:14 + | +LL | pub fn is_maybe_transmutable() + | --------------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` + +error[E0277]: `&mut UnsafeCell` cannot be safely transmuted into `&UnsafeCell` + --> $DIR/unsafecell.rs:46:68 + | +LL | assert::is_maybe_transmutable::<&'static mut UnsafeCell, &'static UnsafeCell>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `UnsafeCell` + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/unsafecell.rs:12:14 + | +LL | pub fn is_maybe_transmutable() + | --------------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`.