1 #![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type)] 2 #![allow(dead_code, unused_variables)] 3 #![deny(rustc::untranslatable_diagnostic)] 4 #![deny(rustc::diagnostic_outside_of_impl)] 5 6 #[macro_use] 7 extern crate tracing; 8 9 pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}; 10 11 pub mod layout; 12 pub(crate) mod maybe_transmutable; 13 14 #[derive(Default)] 15 pub struct Assume { 16 pub alignment: bool, 17 pub lifetimes: bool, 18 pub safety: bool, 19 pub validity: bool, 20 } 21 22 /// Either we have an error, transmutation is allowed, or we have an optional 23 /// Condition that must hold. 24 #[derive(Debug, Hash, Eq, PartialEq, Clone)] 25 pub enum Answer<R> { 26 Yes, 27 No(Reason), 28 If(Condition<R>), 29 } 30 31 /// A condition which must hold for safe transmutation to be possible. 32 #[derive(Debug, Hash, Eq, PartialEq, Clone)] 33 pub enum Condition<R> { 34 /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`. 35 IfTransmutable { src: R, dst: R }, 36 37 /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met. 38 IfAll(Vec<Condition<R>>), 39 40 /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met. 41 IfAny(Vec<Condition<R>>), 42 } 43 44 /// Answers "why wasn't the source type transmutable into the destination type?" 45 #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)] 46 pub enum Reason { 47 /// The layout of the source type is unspecified. 48 SrcIsUnspecified, 49 /// The layout of the destination type is unspecified. 50 DstIsUnspecified, 51 /// The layout of the destination type is bit-incompatible with the source type. 52 DstIsBitIncompatible, 53 /// There aren't any public constructors for `Dst`. 54 DstIsPrivate, 55 /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. 56 DstIsTooBig, 57 /// Src should have a stricter alignment than Dst, but it does not. 58 DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize }, 59 /// Can't go from shared pointer to unique pointer 60 DstIsMoreUnique, 61 /// Encountered a type error 62 TypeError, 63 /// The layout of src is unknown 64 SrcLayoutUnknown, 65 /// The layout of dst is unknown 66 DstLayoutUnknown, 67 } 68 69 #[cfg(feature = "rustc")] 70 mod rustc { 71 use super::*; 72 73 use rustc_hir::lang_items::LangItem; 74 use rustc_infer::infer::InferCtxt; 75 use rustc_macros::TypeVisitable; 76 use rustc_middle::traits::ObligationCause; 77 use rustc_middle::ty::Const; 78 use rustc_middle::ty::ParamEnv; 79 use rustc_middle::ty::Ty; 80 use rustc_middle::ty::TyCtxt; 81 82 /// The source and destination types of a transmutation. 83 #[derive(TypeVisitable, Debug, Clone, Copy)] 84 pub struct Types<'tcx> { 85 /// The source type. 86 pub src: Ty<'tcx>, 87 /// The destination type. 88 pub dst: Ty<'tcx>, 89 } 90 91 pub struct TransmuteTypeEnv<'cx, 'tcx> { 92 infcx: &'cx InferCtxt<'tcx>, 93 } 94 95 impl<'cx, 'tcx> TransmuteTypeEnv<'cx, 'tcx> { new(infcx: &'cx InferCtxt<'tcx>) -> Self96 pub fn new(infcx: &'cx InferCtxt<'tcx>) -> Self { 97 Self { infcx } 98 } 99 100 #[allow(unused)] is_transmutable( &mut self, cause: ObligationCause<'tcx>, types: Types<'tcx>, scope: Ty<'tcx>, assume: crate::Assume, ) -> crate::Answer<crate::layout::rustc::Ref<'tcx>>101 pub fn is_transmutable( 102 &mut self, 103 cause: ObligationCause<'tcx>, 104 types: Types<'tcx>, 105 scope: Ty<'tcx>, 106 assume: crate::Assume, 107 ) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> { 108 crate::maybe_transmutable::MaybeTransmutableQuery::new( 109 types.src, 110 types.dst, 111 scope, 112 assume, 113 self.infcx.tcx, 114 ) 115 .answer() 116 } 117 } 118 119 impl Assume { 120 /// Constructs an `Assume` from a given const-`Assume`. from_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, c: Const<'tcx>, ) -> Option<Self>121 pub fn from_const<'tcx>( 122 tcx: TyCtxt<'tcx>, 123 param_env: ParamEnv<'tcx>, 124 c: Const<'tcx>, 125 ) -> Option<Self> { 126 use rustc_middle::ty::ScalarInt; 127 use rustc_middle::ty::TypeVisitableExt; 128 use rustc_span::symbol::sym; 129 130 let c = c.eval(tcx, param_env); 131 132 if let Err(err) = c.error_reported() { 133 return Some(Self { 134 alignment: true, 135 lifetimes: true, 136 safety: true, 137 validity: true, 138 }); 139 } 140 141 let adt_def = c.ty().ty_adt_def()?; 142 143 assert_eq!( 144 tcx.require_lang_item(LangItem::TransmuteOpts, None), 145 adt_def.did(), 146 "The given `Const` was not marked with the `{}` lang item.", 147 LangItem::TransmuteOpts.name(), 148 ); 149 150 let variant = adt_def.non_enum_variant(); 151 let fields = c.to_valtree().unwrap_branch(); 152 153 let get_field = |name| { 154 let (field_idx, _) = variant 155 .fields 156 .iter() 157 .enumerate() 158 .find(|(_, field_def)| name == field_def.name) 159 .unwrap_or_else(|| panic!("There were no fields named `{name}`.")); 160 fields[field_idx].unwrap_leaf() == ScalarInt::TRUE 161 }; 162 163 Some(Self { 164 alignment: get_field(sym::alignment), 165 lifetimes: get_field(sym::lifetimes), 166 safety: get_field(sym::safety), 167 validity: get_field(sym::validity), 168 }) 169 } 170 } 171 } 172 173 #[cfg(feature = "rustc")] 174 pub use rustc::*; 175