]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/intrinsicck.rs
Auto merge of #104617 - RalfJung:miri, r=RalfJung
[rust.git] / compiler / rustc_hir_typeck / src / intrinsicck.rs
1 use hir::HirId;
2 use rustc_errors::struct_span_err;
3 use rustc_hir as hir;
4 use rustc_index::vec::Idx;
5 use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
6 use rustc_middle::ty::{self, Ty, TyCtxt};
7 use rustc_target::abi::{Pointer, VariantIdx};
8
9 use super::FnCtxt;
10
11 /// If the type is `Option<T>`, it will return `T`, otherwise
12 /// the type itself. Works on most `Option`-like types.
13 fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
14     let ty::Adt(def, substs) = *ty.kind() else { return ty };
15
16     if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() {
17         let data_idx;
18
19         let one = VariantIdx::new(1);
20         let zero = VariantIdx::new(0);
21
22         if def.variant(zero).fields.is_empty() {
23             data_idx = one;
24         } else if def.variant(one).fields.is_empty() {
25             data_idx = zero;
26         } else {
27             return ty;
28         }
29
30         if def.variant(data_idx).fields.len() == 1 {
31             return def.variant(data_idx).fields[0].ty(tcx, substs);
32         }
33     }
34
35     ty
36 }
37
38 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
39     pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
40         let tcx = self.tcx;
41         let span = tcx.hir().span(hir_id);
42         let normalize = |ty| {
43             let ty = self.resolve_vars_if_possible(ty);
44             self.tcx.normalize_erasing_regions(self.param_env, ty)
45         };
46         let from = normalize(from);
47         let to = normalize(to);
48         trace!(?from, ?to);
49
50         // Transmutes that are only changing lifetimes are always ok.
51         if from == to {
52             return;
53         }
54
55         let skel = |ty| SizeSkeleton::compute(ty, tcx, self.param_env);
56         let sk_from = skel(from);
57         let sk_to = skel(to);
58         trace!(?sk_from, ?sk_to);
59
60         // Check for same size using the skeletons.
61         if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
62             if sk_from.same_size(sk_to) {
63                 return;
64             }
65
66             // Special-case transmuting from `typeof(function)` and
67             // `Option<typeof(function)>` to present a clearer error.
68             let from = unpack_option_like(tcx, from);
69             if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&tcx) {
70                 struct_span_err!(tcx.sess, span, E0591, "can't transmute zero-sized type")
71                     .note(&format!("source type: {from}"))
72                     .note(&format!("target type: {to}"))
73                     .help("cast with `as` to a pointer instead")
74                     .emit();
75                 return;
76             }
77         }
78
79         // Try to display a sensible error with as much information as possible.
80         let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
81             Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()),
82             Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
83             Err(LayoutError::Unknown(bad)) => {
84                 if bad == ty {
85                     "this type does not have a fixed size".to_owned()
86                 } else {
87                     format!("size can vary because of {bad}")
88                 }
89             }
90             Err(err) => err.to_string(),
91         };
92
93         let mut err = struct_span_err!(
94             tcx.sess,
95             span,
96             E0512,
97             "cannot transmute between types of different sizes, \
98                                         or dependently-sized types"
99         );
100         if from == to {
101             err.note(&format!("`{from}` does not have a fixed size"));
102         } else {
103             err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
104                 .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
105         }
106         err.emit();
107     }
108 }