rustc_transmute/
lib.rs

1// tidy-alphabetical-start
2#![cfg_attr(test, feature(test))]
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, T> {
23    Yes,
24    No(Reason<T>),
25    If(Condition<R, T>),
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, T> {
31    /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`.
32    Transmutable { src: T, dst: T },
33
34    /// The region `long` must outlive `short`.
35    Outlives { long: R, short: R },
36
37    /// The `ty` is immutable.
38    Immutable { ty: T },
39
40    /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met.
41    IfAll(Vec<Condition<R, T>>),
42
43    /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met.
44    IfAny(Vec<Condition<R, T>>),
45}
46
47/// Answers "why wasn't the source type transmutable into the destination type?"
48#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
49pub enum Reason<T> {
50    /// The layout of the source type is not yet supported.
51    SrcIsNotYetSupported,
52    /// The layout of the destination type is not yet supported.
53    DstIsNotYetSupported,
54    /// The layout of the destination type is bit-incompatible with the source type.
55    DstIsBitIncompatible,
56    /// The destination type is uninhabited.
57    DstUninhabited,
58    /// The destination type may carry safety invariants.
59    DstMayHaveSafetyInvariants,
60    /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
61    DstIsTooBig,
62    /// `Dst` is larger `Src`.
63    DstRefIsTooBig {
64        /// The referent of the source type.
65        src: T,
66        /// The size of the source type's referent.
67        src_size: usize,
68        /// The too-large referent of the destination type.
69        dst: T,
70        /// The size of the destination type's referent.
71        dst_size: usize,
72    },
73    /// Src should have a stricter alignment than Dst, but it does not.
74    DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
75    /// Can't go from shared pointer to unique pointer
76    DstIsMoreUnique,
77    /// Encountered a type error
78    TypeError,
79    /// The layout of src is unknown
80    SrcLayoutUnknown,
81    /// The layout of dst is unknown
82    DstLayoutUnknown,
83    /// The size of src is overflow
84    SrcSizeOverflow,
85    /// The size of dst is overflow
86    DstSizeOverflow,
87}
88
89#[cfg(feature = "rustc")]
90mod rustc {
91    use rustc_hir::lang_items::LangItem;
92    use rustc_middle::ty::{Const, Region, Ty, TyCtxt};
93
94    use super::*;
95
96    pub struct TransmuteTypeEnv<'tcx> {
97        tcx: TyCtxt<'tcx>,
98    }
99
100    impl<'tcx> TransmuteTypeEnv<'tcx> {
101        pub fn new(tcx: TyCtxt<'tcx>) -> Self {
102            Self { tcx }
103        }
104
105        pub fn is_transmutable(
106            &mut self,
107            src: Ty<'tcx>,
108            dst: Ty<'tcx>,
109            assume: crate::Assume,
110        ) -> crate::Answer<Region<'tcx>, Ty<'tcx>> {
111            crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, self.tcx)
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 cv = ct.try_to_value()?;
123            let adt_def = cv.ty.ty_adt_def()?;
124
125            if !tcx.is_lang_item(adt_def.did(), LangItem::TransmuteOpts) {
126                tcx.dcx().delayed_bug(format!(
127                    "The given `const` was not marked with the `{}` lang item.",
128                    LangItem::TransmuteOpts.name()
129                ));
130                return Some(Self {
131                    alignment: true,
132                    lifetimes: true,
133                    safety: true,
134                    validity: true,
135                });
136            }
137
138            let variant = adt_def.non_enum_variant();
139            let fields = cv.to_branch();
140
141            let get_field = |name| {
142                let (field_idx, _) = variant
143                    .fields
144                    .iter()
145                    .enumerate()
146                    .find(|(_, field_def)| name == field_def.name)
147                    .unwrap_or_else(|| panic!("There were no fields named `{name}`."));
148                fields[field_idx].try_to_leaf().map(|leaf| leaf == ScalarInt::TRUE)
149            };
150
151            Some(Self {
152                alignment: get_field(sym::alignment)?,
153                lifetimes: get_field(sym::lifetimes)?,
154                safety: get_field(sym::safety)?,
155                validity: get_field(sym::validity)?,
156            })
157        }
158    }
159}
160
161#[cfg(feature = "rustc")]
162pub use rustc::*;