2 use rustc_errors::struct_span_err;
4 use rustc_index::vec::Idx;
5 use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
6 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
7 use rustc_target::abi::{Pointer, VariantIdx};
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 };
16 if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() {
19 let one = VariantIdx::new(1);
20 let zero = VariantIdx::new(0);
22 if def.variant(zero).fields.is_empty() {
24 } else if def.variant(one).fields.is_empty() {
30 if def.variant(data_idx).fields.len() == 1 {
31 return def.variant(data_idx).fields[0].ty(tcx, substs);
38 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
39 pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
41 let dl = &tcx.data_layout;
42 let span = tcx.hir().span(hir_id);
43 let normalize = |ty| {
44 let ty = self.resolve_vars_if_possible(ty);
45 self.tcx.normalize_erasing_regions(self.param_env, ty)
47 let from = normalize(from);
48 let to = normalize(to);
50 if from.has_non_region_infer() || to.has_non_region_infer() {
51 tcx.sess.delay_span_bug(span, "argument to transmute has inference variables");
54 // Transmutes that are only changing lifetimes are always ok.
59 let skel = |ty| SizeSkeleton::compute(ty, tcx, self.param_env);
60 let sk_from = skel(from);
62 trace!(?sk_from, ?sk_to);
64 // Check for same size using the skeletons.
65 if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
66 if sk_from.same_size(sk_to) {
70 // Special-case transmuting from `typeof(function)` and
71 // `Option<typeof(function)>` to present a clearer error.
72 let from = unpack_option_like(tcx, from);
73 if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer(dl.instruction_address_space).size(&tcx) {
74 struct_span_err!(tcx.sess, span, E0591, "can't transmute zero-sized type")
75 .note(&format!("source type: {from}"))
76 .note(&format!("target type: {to}"))
77 .help("cast with `as` to a pointer instead")
83 // Try to display a sensible error with as much information as possible.
84 let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
85 Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()),
86 Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
87 Err(LayoutError::Unknown(bad)) => {
89 "this type does not have a fixed size".to_owned()
91 format!("size can vary because of {bad}")
94 Err(err) => err.to_string(),
97 let mut err = struct_span_err!(
101 "cannot transmute between types of different sizes, \
102 or dependently-sized types"
105 err.note(&format!("`{from}` does not have a fixed size"));
107 err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
108 .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
109 let mut should_delay_as_bug = false;
110 if let Err(LayoutError::Unknown(bad_from)) = sk_from && bad_from.references_error() {
111 should_delay_as_bug = true;
113 if let Err(LayoutError::Unknown(bad_to)) = sk_to && bad_to.references_error() {
114 should_delay_as_bug = true;
116 if should_delay_as_bug {