]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_transmute/src/lib.rs
Auto merge of #100726 - jswrenn:transmute, r=oli-obk
[rust.git] / compiler / rustc_transmute / src / lib.rs
1 #![feature(alloc_layout_extra, control_flow_enum, 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(crate) 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 /// The type encodes answers to the question: "Are these types transmutable?"
23 #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
24 pub enum Answer<R>
25 where
26     R: layout::Ref,
27 {
28     /// `Src` is transmutable into `Dst`.
29     Yes,
30
31     /// `Src` is NOT transmutable into `Dst`.
32     No(Reason),
33
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<Answer<R>>),
39
40     /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met.
41     IfAny(Vec<Answer<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 }
58
59 #[cfg(feature = "rustc")]
60 mod rustc {
61     use super::*;
62
63     use rustc_hir::lang_items::LangItem;
64     use rustc_infer::infer::InferCtxt;
65     use rustc_macros::{TypeFoldable, TypeVisitable};
66     use rustc_middle::traits::ObligationCause;
67     use rustc_middle::ty::Binder;
68     use rustc_middle::ty::Const;
69     use rustc_middle::ty::ParamEnv;
70     use rustc_middle::ty::Ty;
71     use rustc_middle::ty::TyCtxt;
72
73     /// The source and destination types of a transmutation.
74     #[derive(TypeFoldable, TypeVisitable, Debug, Clone, Copy)]
75     pub struct Types<'tcx> {
76         /// The source type.
77         pub src: Ty<'tcx>,
78         /// The destination type.
79         pub dst: Ty<'tcx>,
80     }
81
82     pub struct TransmuteTypeEnv<'cx, 'tcx> {
83         infcx: &'cx InferCtxt<'cx, 'tcx>,
84     }
85
86     impl<'cx, 'tcx> TransmuteTypeEnv<'cx, 'tcx> {
87         pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> Self {
88             Self { infcx }
89         }
90
91         #[allow(unused)]
92         pub fn is_transmutable(
93             &mut self,
94             cause: ObligationCause<'tcx>,
95             src_and_dst: Binder<'tcx, Types<'tcx>>,
96             scope: Ty<'tcx>,
97             assume: crate::Assume,
98         ) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> {
99             let src = src_and_dst.map_bound(|types| types.src).skip_binder();
100             let dst = src_and_dst.map_bound(|types| types.dst).skip_binder();
101             crate::maybe_transmutable::MaybeTransmutableQuery::new(
102                 src,
103                 dst,
104                 scope,
105                 assume,
106                 self.infcx.tcx,
107             )
108             .answer()
109         }
110     }
111
112     impl Assume {
113         /// Constructs an `Assume` from a given const-`Assume`.
114         pub fn from_const<'tcx>(
115             tcx: TyCtxt<'tcx>,
116             param_env: ParamEnv<'tcx>,
117             c: Const<'tcx>,
118         ) -> Self {
119             use rustc_middle::ty::ScalarInt;
120             use rustc_middle::ty::TypeVisitable;
121             use rustc_span::symbol::sym;
122
123             let c = c.eval(tcx, param_env);
124
125             if let Some(err) = c.error_reported() {
126                 return Self { alignment: true, lifetimes: true, safety: true, validity: true };
127             }
128
129             let adt_def = c.ty().ty_adt_def().expect("The given `Const` must be an ADT.");
130
131             assert_eq!(
132                 tcx.require_lang_item(LangItem::TransmuteOpts, None),
133                 adt_def.did(),
134                 "The given `Const` was not marked with the `{}` lang item.",
135                 LangItem::TransmuteOpts.name(),
136             );
137
138             let variant = adt_def.non_enum_variant();
139             let fields = c.to_valtree().unwrap_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                     .expect(&format!("There were no fields named `{name}`."));
148                 fields[field_idx].unwrap_leaf() == ScalarInt::TRUE
149             };
150
151             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")]
162 pub use rustc::*;