rustc_hir_typeck/
intrinsicck.rs
1use hir::HirId;
2use rustc_abi::Primitive::Pointer;
3use rustc_abi::VariantIdx;
4use rustc_errors::codes::*;
5use rustc_errors::struct_span_code_err;
6use rustc_hir as hir;
7use rustc_index::Idx;
8use rustc_middle::bug;
9use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
10use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
11use tracing::trace;
12
13use super::FnCtxt;
14
15fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
18 let ty::Adt(def, args) = *ty.kind() else { return ty };
19
20 if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() {
21 let data_idx;
22
23 let one = VariantIdx::new(1);
24 let zero = VariantIdx::ZERO;
25
26 if def.variant(zero).fields.is_empty() {
27 data_idx = one;
28 } else if def.variant(one).fields.is_empty() {
29 data_idx = zero;
30 } else {
31 return ty;
32 }
33
34 if def.variant(data_idx).fields.len() == 1 {
35 return def.variant(data_idx).single_field().ty(tcx, args);
36 }
37 }
38
39 ty
40}
41
42impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
43 pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
46 let tcx = self.tcx;
47 let dl = &tcx.data_layout;
48 let span = tcx.hir().span(hir_id);
49 let normalize = |ty| {
50 let ty = self.resolve_vars_if_possible(ty);
51 if let Ok(ty) =
52 self.tcx.try_normalize_erasing_regions(self.typing_env(self.param_env), ty)
53 {
54 ty
55 } else {
56 Ty::new_error_with_message(
57 tcx,
58 span,
59 "tried to normalize non-wf type in check_transmute",
60 )
61 }
62 };
63 let from = normalize(from);
64 let to = normalize(to);
65 trace!(?from, ?to);
66 if from.has_non_region_infer() || to.has_non_region_infer() {
67 self.dcx().span_bug(span, "argument to transmute has inference variables");
71 }
72 if from == to {
74 return;
75 }
76
77 let skel = |ty| SizeSkeleton::compute(ty, tcx, self.typing_env(self.param_env));
78 let sk_from = skel(from);
79 let sk_to = skel(to);
80 trace!(?sk_from, ?sk_to);
81
82 if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
84 if sk_from.same_size(sk_to) {
85 return;
86 }
87
88 let from = unpack_option_like(tcx, from);
91 if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to)
92 && size_to == Pointer(dl.instruction_address_space).size(&tcx)
93 {
94 struct_span_code_err!(self.dcx(), span, E0591, "can't transmute zero-sized type")
95 .with_note(format!("source type: {from}"))
96 .with_note(format!("target type: {to}"))
97 .with_help("cast with `as` to a pointer instead")
98 .emit();
99 return;
100 }
101 }
102
103 let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
105 Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
106 Ok(SizeSkeleton::Known(size, _)) => {
107 if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
108 format!("{v} bits")
109 } else {
110 bug!("{:?} overflow for u128", size)
114 }
115 }
116 Ok(SizeSkeleton::Generic(size)) => {
117 if let Some(size) =
118 self.try_structurally_resolve_const(span, size).try_to_target_usize(tcx)
119 {
120 format!("{size} bytes")
121 } else {
122 format!("generic size {size}")
123 }
124 }
125 Err(LayoutError::TooGeneric(bad)) => {
126 if *bad == ty {
127 "this type does not have a fixed size".to_owned()
128 } else {
129 format!("size can vary because of {bad}")
130 }
131 }
132 Err(err) => err.to_string(),
133 };
134
135 let mut err = struct_span_code_err!(
136 self.dcx(),
137 span,
138 E0512,
139 "cannot transmute between types of different sizes, \
140 or dependently-sized types"
141 );
142 if from == to {
143 err.note(format!("`{from}` does not have a fixed size"));
144 err.emit();
145 } else {
146 err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
147 .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
148 if let Err(LayoutError::ReferencesError(_)) = sk_from {
149 err.delay_as_bug();
150 } else if let Err(LayoutError::ReferencesError(_)) = sk_to {
151 err.delay_as_bug();
152 } else {
153 err.emit();
154 }
155 }
156 }
157}