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    /// The source and destination types of a transmutation.
97    #[derive(Debug, Clone, Copy)]
98    pub struct Types<'tcx> {
99        /// The source type.
100        pub src: Ty<'tcx>,
101        /// The destination type.
102        pub dst: Ty<'tcx>,
103    }
104
105    pub struct TransmuteTypeEnv<'tcx> {
106        tcx: TyCtxt<'tcx>,
107    }
108
109    impl<'tcx> TransmuteTypeEnv<'tcx> {
110        pub fn new(tcx: TyCtxt<'tcx>) -> Self {
111            Self { tcx }
112        }
113
114        pub fn is_transmutable(
115            &mut self,
116            types: Types<'tcx>,
117            assume: crate::Assume,
118        ) -> crate::Answer<Region<'tcx>, Ty<'tcx>> {
119            crate::maybe_transmutable::MaybeTransmutableQuery::new(
120                types.src, types.dst, assume, self.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>(tcx: TyCtxt<'tcx>, ct: Const<'tcx>) -> Option<Self> {
129            use rustc_middle::ty::ScalarInt;
130            use rustc_span::sym;
131
132            let Some(cv) = ct.try_to_value() else {
133                return None;
134            };
135
136            let adt_def = cv.ty.ty_adt_def()?;
137
138            if !tcx.is_lang_item(adt_def.did(), LangItem::TransmuteOpts) {
139                tcx.dcx().delayed_bug(format!(
140                    "The given `const` was not marked with the `{}` lang item.",
141                    LangItem::TransmuteOpts.name()
142                ));
143                return Some(Self {
144                    alignment: true,
145                    lifetimes: true,
146                    safety: true,
147                    validity: true,
148                });
149            }
150
151            let variant = adt_def.non_enum_variant();
152            let fields = cv.valtree.unwrap_branch();
153
154            let get_field = |name| {
155                let (field_idx, _) = variant
156                    .fields
157                    .iter()
158                    .enumerate()
159                    .find(|(_, field_def)| name == field_def.name)
160                    .unwrap_or_else(|| panic!("There were no fields named `{name}`."));
161                fields[field_idx].unwrap_leaf() == ScalarInt::TRUE
162            };
163
164            Some(Self {
165                alignment: get_field(sym::alignment),
166                lifetimes: get_field(sym::lifetimes),
167                safety: get_field(sym::safety),
168                validity: get_field(sym::validity),
169            })
170        }
171    }
172}
173
174#[cfg(feature = "rustc")]
175pub use rustc::*;