rustc_transmute/
lib.rs

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