rustc_transmute/
lib.rs

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