1 use super::TRANSMUTE_UNDEFINED_REPR;
2 use clippy_utils::diagnostics::span_lint_and_then;
3 use clippy_utils::ty::is_c_void;
5 use rustc_lint::LateContext;
6 use rustc_middle::ty::subst::Subst;
7 use rustc_middle::ty::{self, Ty, TypeAndMut};
10 #[allow(clippy::too_many_lines)]
11 pub(super) fn check<'tcx>(
12 cx: &LateContext<'tcx>,
14 from_ty_orig: Ty<'tcx>,
17 let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
18 let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
20 while from_ty != to_ty {
21 match reduce_refs(cx, e.span, from_ty, to_ty) {
22 ReducedTys::FromFatPtr {
25 } => match reduce_ty(cx, to_sub_ty) {
26 ReducedTy::IntArray | ReducedTy::TypeErasure => break,
27 ReducedTy::Ref(to_sub_ty) => {
35 TRANSMUTE_UNDEFINED_REPR,
37 &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
39 if from_ty_orig.peel_refs() != unsized_ty {
40 diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
47 ReducedTys::ToFatPtr {
50 } => match reduce_ty(cx, from_sub_ty) {
51 ReducedTy::IntArray | ReducedTy::TypeErasure => break,
52 ReducedTy::Ref(from_sub_ty) => {
53 from_ty = from_sub_ty;
60 TRANSMUTE_UNDEFINED_REPR,
62 &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
64 if to_ty_orig.peel_refs() != unsized_ty {
65 diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
75 } => match reduce_ty(cx, from_sub_ty) {
76 ReducedTy::UnorderedFields(from_ty) => {
79 TRANSMUTE_UNDEFINED_REPR,
81 &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
83 if from_ty_orig.peel_refs() != from_ty {
84 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
90 ReducedTy::Ref(from_sub_ty) => {
91 from_ty = from_sub_ty;
100 } => match reduce_ty(cx, to_sub_ty) {
101 ReducedTy::UnorderedFields(to_ty) => {
104 TRANSMUTE_UNDEFINED_REPR,
106 &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
108 if to_ty_orig.peel_refs() != to_ty {
109 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
115 ReducedTy::Ref(to_sub_ty) => {
116 from_ty = from_sub_ty;
123 from_ty: from_sub_ty,
125 } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
126 (ReducedTy::IntArray | ReducedTy::TypeErasure, _)
127 | (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false,
128 (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
131 TRANSMUTE_UNDEFINED_REPR,
134 "transmute from `{}` to `{}`, both of which have an undefined layout",
135 from_ty_orig, to_ty_orig
139 if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def());
140 if from_def == to_def;
143 "two instances of the same generic type (`{}`) may have different layouts",
144 cx.tcx.item_name(from_def.did())
147 if from_ty_orig.peel_refs() != from_ty {
148 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
150 if to_ty_orig.peel_refs() != to_ty {
151 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
160 ReducedTy::UnorderedFields(from_ty),
161 ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
165 TRANSMUTE_UNDEFINED_REPR,
167 &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
169 if from_ty_orig.peel_refs() != from_ty {
170 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
177 ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
178 ReducedTy::UnorderedFields(to_ty),
182 TRANSMUTE_UNDEFINED_REPR,
184 &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
186 if to_ty_orig.peel_refs() != to_ty {
187 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
193 (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
194 from_ty = from_sub_ty;
199 ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
200 ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
202 | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
210 enum ReducedTys<'tcx> {
211 FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
212 ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
213 ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
214 FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
215 Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
218 /// Remove references so long as both types are references.
219 fn reduce_refs<'tcx>(
220 cx: &LateContext<'tcx>,
222 mut from_ty: Ty<'tcx>,
224 ) -> ReducedTys<'tcx> {
226 return match (from_ty.kind(), to_ty.kind()) {
228 &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
229 &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
231 from_ty = from_sub_ty;
235 (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
236 if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
238 ReducedTys::FromFatPtr { unsized_ty, to_ty }
240 (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
241 if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
243 ReducedTys::ToFatPtr { unsized_ty, from_ty }
245 (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => {
246 ReducedTys::FromPtr { from_ty, to_ty }
248 (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => {
249 ReducedTys::ToPtr { from_ty, to_ty }
251 _ => ReducedTys::Other { from_ty, to_ty },
256 enum ReducedTy<'tcx> {
257 /// The type can be used for type erasure.
259 /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
260 /// sized fields with a defined order.
261 OrderedFields(Ty<'tcx>),
262 /// The type is a struct containing multiple non-zero sized fields with no defined order.
263 UnorderedFields(Ty<'tcx>),
264 /// The type is a reference to the contained type.
266 /// The type is an array of a primitive integer type. These can be used as storage for a value
273 /// Reduce structs containing a single non-zero sized field to it's contained type.
274 fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
276 ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
277 return match *ty.kind() {
278 ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray,
279 ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
283 ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
285 let Some(sized_ty) = args.iter().find(|&ty| !is_zero_sized_ty(cx, ty)) else {
286 return ReducedTy::OrderedFields(ty);
288 if args.iter().all(|ty| is_zero_sized_ty(cx, ty)) {
292 ReducedTy::UnorderedFields(ty)
294 ty::Adt(def, substs) if def.is_struct() => {
299 .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
300 let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
301 return ReducedTy::TypeErasure;
303 if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
307 if def.repr().inhibit_struct_field_reordering_opt() {
308 ReducedTy::OrderedFields(ty)
310 ReducedTy::UnorderedFields(ty)
313 ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
314 ReducedTy::TypeErasure
316 ty::Foreign(_) => ReducedTy::TypeErasure,
317 ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
318 ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
319 _ => ReducedTy::Other(ty),
324 fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
326 if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty);
327 if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
329 layout.layout.size().bytes() == 0