summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_transmute/src/lib.rs
blob: cfc7c752a6bd60bf6c4d5444faa8568c41885dee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#![feature(
    alloc_layout_extra,
    control_flow_enum,
    decl_macro,
    iterator_try_reduce,
    never_type,
    result_into_ok_or_err
)]
#![allow(dead_code, unused_variables)]

#[macro_use]
extern crate tracing;

#[cfg(feature = "rustc")]
pub(crate) use rustc_data_structures::fx::{FxHashMap as Map, FxHashSet as Set};

#[cfg(not(feature = "rustc"))]
pub(crate) use std::collections::{HashMap as Map, HashSet as Set};

pub(crate) mod layout;
pub(crate) mod maybe_transmutable;

#[derive(Default)]
pub struct Assume {
    pub alignment: bool,
    pub lifetimes: bool,
    pub validity: bool,
    pub visibility: bool,
}

/// The type encodes answers to the question: "Are these types transmutable?"
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
pub enum Answer<R>
where
    R: layout::Ref,
{
    /// `Src` is transmutable into `Dst`.
    Yes,

    /// `Src` is NOT transmutable into `Dst`.
    No(Reason),

    /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`.
    IfTransmutable { src: R, dst: R },

    /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met.
    IfAll(Vec<Answer<R>>),

    /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met.
    IfAny(Vec<Answer<R>>),
}

/// Answers: Why wasn't the source type transmutable into the destination type?
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
pub enum Reason {
    /// The layout of the source type is unspecified.
    SrcIsUnspecified,
    /// The layout of the destination type is unspecified.
    DstIsUnspecified,
    /// The layout of the destination type is bit-incompatible with the source type.
    DstIsBitIncompatible,
    /// There aren't any public constructors for `Dst`.
    DstIsPrivate,
    /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
    DstIsTooBig,
}

#[cfg(feature = "rustc")]
mod rustc {
    use rustc_infer::infer::InferCtxt;
    use rustc_macros::{TypeFoldable, TypeVisitable};
    use rustc_middle::traits::ObligationCause;
    use rustc_middle::ty::Binder;
    use rustc_middle::ty::Ty;

    /// The source and destination types of a transmutation.
    #[derive(TypeFoldable, TypeVisitable, Debug, Clone, Copy)]
    pub struct Types<'tcx> {
        /// The source type.
        pub src: Ty<'tcx>,
        /// The destination type.
        pub dst: Ty<'tcx>,
    }

    pub struct TransmuteTypeEnv<'cx, 'tcx> {
        infcx: &'cx InferCtxt<'cx, 'tcx>,
    }

    impl<'cx, 'tcx> TransmuteTypeEnv<'cx, 'tcx> {
        pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> Self {
            Self { infcx }
        }

        #[allow(unused)]
        pub fn is_transmutable(
            &mut self,
            cause: ObligationCause<'tcx>,
            src_and_dst: Binder<'tcx, Types<'tcx>>,
            scope: Ty<'tcx>,
            assume: crate::Assume,
        ) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> {
            let src = src_and_dst.map_bound(|types| types.src).skip_binder();
            let dst = src_and_dst.map_bound(|types| types.dst).skip_binder();
            crate::maybe_transmutable::MaybeTransmutableQuery::new(
                src,
                dst,
                scope,
                assume,
                self.infcx.tcx,
            )
            .answer()
        }
    }
}

#[cfg(feature = "rustc")]
pub use rustc::*;