Skip to content

Commit 29ee5e2

Browse files
committed
Auto merge of #8440 - Jarcho:transmute_undefined, r=Manishearth
Some more fixes for `transmute_undefined_repr` changelog: Fix transmuting a struct containing a pointer into a pointer in `transmute_undefined_repr` changelog: Allow various forms of type erasure in `transmute_undefined_repr`
2 parents 73367f8 + d28d19d commit 29ee5e2

File tree

6 files changed

+196
-93
lines changed

6 files changed

+196
-93
lines changed

clippy_lints/src/casts/cast_ptr_alignment.rs

+1-17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use clippy_utils::diagnostics::span_lint;
22
use clippy_utils::is_hir_ty_cfg_dependant;
3+
use clippy_utils::ty::is_c_void;
34
use if_chain::if_chain;
45
use rustc_hir::{Expr, ExprKind, GenericArg};
56
use rustc_lint::LateContext;
67
use rustc_middle::ty::layout::LayoutOf;
78
use rustc_middle::ty::{self, Ty};
8-
use rustc_span::symbol::sym;
99

1010
use super::CAST_PTR_ALIGNMENT;
1111

@@ -62,19 +62,3 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f
6262
}
6363
}
6464
}
65-
66-
/// Check if the given type is either `core::ffi::c_void` or
67-
/// one of the platform specific `libc::<platform>::c_void` of libc.
68-
fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
69-
if let ty::Adt(adt, _) = ty.kind() {
70-
let names = cx.get_def_path(adt.did);
71-
72-
if names.is_empty() {
73-
return false;
74-
}
75-
if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
76-
return true;
77-
}
78-
}
79-
false
80-
}

clippy_lints/src/transmute/transmute_undefined_repr.rs

+75-34
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::TRANSMUTE_UNDEFINED_REPR;
22
use clippy_utils::diagnostics::span_lint_and_then;
3+
use clippy_utils::ty::is_c_void;
34
use rustc_hir::Expr;
45
use rustc_lint::LateContext;
56
use rustc_middle::ty::subst::{GenericArg, Subst};
@@ -18,33 +19,55 @@ pub(super) fn check<'tcx>(
1819

1920
while from_ty != to_ty {
2021
match reduce_refs(cx, e.span, from_ty, to_ty) {
21-
ReducedTys::FromFatPtr { unsized_ty, .. } => {
22-
span_lint_and_then(
23-
cx,
24-
TRANSMUTE_UNDEFINED_REPR,
25-
e.span,
26-
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
27-
|diag| {
28-
if from_ty_orig.peel_refs() != unsized_ty {
29-
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
30-
}
31-
},
32-
);
33-
return true;
22+
ReducedTys::FromFatPtr {
23+
unsized_ty,
24+
to_ty: to_sub_ty,
25+
} => match reduce_ty(cx, to_sub_ty) {
26+
ReducedTy::IntArray | ReducedTy::TypeErasure => break,
27+
ReducedTy::Ref(to_sub_ty) => {
28+
from_ty = unsized_ty;
29+
to_ty = to_sub_ty;
30+
continue;
31+
},
32+
_ => {
33+
span_lint_and_then(
34+
cx,
35+
TRANSMUTE_UNDEFINED_REPR,
36+
e.span,
37+
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
38+
|diag| {
39+
if from_ty_orig.peel_refs() != unsized_ty {
40+
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
41+
}
42+
},
43+
);
44+
return true;
45+
},
3446
},
35-
ReducedTys::ToFatPtr { unsized_ty, .. } => {
36-
span_lint_and_then(
37-
cx,
38-
TRANSMUTE_UNDEFINED_REPR,
39-
e.span,
40-
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
41-
|diag| {
42-
if to_ty_orig.peel_refs() != unsized_ty {
43-
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
44-
}
45-
},
46-
);
47-
return true;
47+
ReducedTys::ToFatPtr {
48+
unsized_ty,
49+
from_ty: from_sub_ty,
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;
54+
to_ty = unsized_ty;
55+
continue;
56+
},
57+
_ => {
58+
span_lint_and_then(
59+
cx,
60+
TRANSMUTE_UNDEFINED_REPR,
61+
e.span,
62+
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
63+
|diag| {
64+
if to_ty_orig.peel_refs() != unsized_ty {
65+
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
66+
}
67+
},
68+
);
69+
return true;
70+
},
4871
},
4972
ReducedTys::ToPtr {
5073
from_ty: from_sub_ty,
@@ -100,7 +123,8 @@ pub(super) fn check<'tcx>(
100123
from_ty: from_sub_ty,
101124
to_ty: to_sub_ty,
102125
} => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
103-
(ReducedTy::IntArray, _) | (_, ReducedTy::IntArray) => return false,
126+
(ReducedTy::IntArray | ReducedTy::TypeErasure, _)
127+
| (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false,
104128
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
105129
span_lint_and_then(
106130
cx,
@@ -182,13 +206,14 @@ pub(super) fn check<'tcx>(
182206
}
183207

184208
enum ReducedTys<'tcx> {
185-
FromFatPtr { unsized_ty: Ty<'tcx> },
186-
ToFatPtr { unsized_ty: Ty<'tcx> },
209+
FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
210+
ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
187211
ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
188212
FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
189213
Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
190214
}
191215

216+
/// Remove references so long as both types are references.
192217
fn reduce_refs<'tcx>(
193218
cx: &LateContext<'tcx>,
194219
span: Span,
@@ -208,12 +233,12 @@ fn reduce_refs<'tcx>(
208233
(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }), _)
209234
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
210235
{
211-
ReducedTys::FromFatPtr { unsized_ty }
236+
ReducedTys::FromFatPtr { unsized_ty, to_ty }
212237
},
213238
(_, ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))
214239
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
215240
{
216-
ReducedTys::ToFatPtr { unsized_ty }
241+
ReducedTys::ToFatPtr { unsized_ty, from_ty }
217242
},
218243
(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. }), _) => {
219244
ReducedTys::FromPtr { from_ty, to_ty }
@@ -227,13 +252,23 @@ fn reduce_refs<'tcx>(
227252
}
228253

229254
enum ReducedTy<'tcx> {
255+
/// The type can be used for type erasure.
256+
TypeErasure,
257+
/// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
258+
/// sized fields with a defined order.
230259
OrderedFields(Ty<'tcx>),
260+
/// The type is a struct containing multiple non-zero sized fields with no defined order.
231261
UnorderedFields(Ty<'tcx>),
262+
/// The type is a reference to the contained type.
232263
Ref(Ty<'tcx>),
233-
Other(Ty<'tcx>),
264+
/// The type is an array of a primitive integer type. These can be used as storage for a value
265+
/// of another type.
234266
IntArray,
267+
/// Any other type.
268+
Other(Ty<'tcx>),
235269
}
236270

271+
/// Reduce structs containing a single non-zero sized field to it's contained type.
237272
fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
238273
loop {
239274
ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
@@ -243,6 +278,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
243278
ty = sub_ty;
244279
continue;
245280
},
281+
ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
246282
ty::Tuple(args) => {
247283
let mut iter = args.iter().map(GenericArg::expect_ty);
248284
let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, ty)) else {
@@ -261,7 +297,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
261297
.iter()
262298
.map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
263299
let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, ty)) else {
264-
return ReducedTy::OrderedFields(ty);
300+
return ReducedTy::TypeErasure;
265301
};
266302
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
267303
ty = sized_ty;
@@ -273,7 +309,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
273309
ReducedTy::UnorderedFields(ty)
274310
}
275311
},
276-
ty::Ref(..) | ty::RawPtr(_) => ReducedTy::Ref(ty),
312+
ty::Adt(def, _) if def.is_enum() && (def.variants.is_empty() || is_c_void(cx, ty)) => {
313+
ReducedTy::TypeErasure
314+
},
315+
ty::Foreign(_) => ReducedTy::TypeErasure,
316+
ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
317+
ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
277318
_ => ReducedTy::Other(ty),
278319
};
279320
}

clippy_utils/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![feature(box_patterns)]
22
#![feature(control_flow_enum)]
33
#![feature(let_else)]
4+
#![feature(let_chains)]
45
#![feature(once_cell)]
56
#![feature(rustc_private)]
67
#![recursion_limit = "512"]

clippy_utils/src/ty.rs

+14
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,17 @@ pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: &'_ AdtDef, i: VariantIdx) -
572572
},
573573
}
574574
}
575+
576+
/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
577+
/// platform specific `libc::<platform>::c_void` types in libc.
578+
pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
579+
if let ty::Adt(adt, _) = ty.kind()
580+
&& let &[krate, .., name] = &*cx.get_def_path(adt.did)
581+
&& let sym::libc | sym::core | sym::std = krate
582+
&& name.as_str() == "c_void"
583+
{
584+
true
585+
} else {
586+
false
587+
}
588+
}

tests/ui/transmute_undefined_repr.rs

+64-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#![warn(clippy::transmute_undefined_repr)]
2-
#![allow(clippy::unit_arg)]
2+
#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)]
3+
4+
use core::ffi::c_void;
5+
use core::mem::{size_of, transmute};
36

47
fn value<T>() -> T {
58
unimplemented!()
@@ -14,35 +17,75 @@ struct Ty2C<T, U>(T, U);
1417

1518
fn main() {
1619
unsafe {
17-
let _: () = core::mem::transmute(value::<Empty>());
18-
let _: Empty = core::mem::transmute(value::<()>());
20+
let _: () = transmute(value::<Empty>());
21+
let _: Empty = transmute(value::<()>());
1922

20-
let _: Ty<u32> = core::mem::transmute(value::<u32>());
21-
let _: Ty<u32> = core::mem::transmute(value::<u32>());
23+
let _: Ty<u32> = transmute(value::<u32>());
24+
let _: Ty<u32> = transmute(value::<u32>());
2225

23-
let _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
24-
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
26+
let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
27+
let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
2528

26-
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
27-
let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
29+
let _: Ty2<u32, i32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
30+
let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
2831

29-
let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
30-
let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
32+
let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
33+
let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
3134

32-
let _: Ty<&()> = core::mem::transmute(value::<&()>());
33-
let _: &() = core::mem::transmute(value::<Ty<&()>>());
35+
let _: Ty<&()> = transmute(value::<&()>());
36+
let _: &() = transmute(value::<Ty<&()>>());
3437

35-
let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
36-
let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
38+
let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
39+
let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
3740

38-
let _: Ty<usize> = core::mem::transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
39-
let _: &Ty2<u32, i32> = core::mem::transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
41+
let _: Ty<usize> = transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
42+
let _: &Ty2<u32, i32> = transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
4043

41-
let _: Ty<[u8; 8]> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
42-
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
44+
let _: Ty<[u8; 8]> = transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
45+
let _: Ty2<u32, i32> = transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
4346

4447
// issue #8417
45-
let _: Ty2C<Ty2<u32, i32>, ()> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
46-
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<Ty2<u32, i32>, ()>>()); // Ok, Ty2 types are the same
48+
let _: Ty2C<Ty2<u32, i32>, ()> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
49+
let _: Ty2<u32, i32> = transmute(value::<Ty2C<Ty2<u32, i32>, ()>>()); // Ok, Ty2 types are the same
50+
51+
let _: &'static mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
52+
let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
53+
let _: *mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
54+
let _: Box<Ty2<u32, u32>> = transmute(value::<*mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
55+
56+
let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
57+
let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
58+
59+
let _: *const () = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
60+
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const ()>()); // Ok, reverse type erasure
61+
62+
let _: *const c_void = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
63+
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const c_void>()); // Ok, reverse type erasure
64+
65+
enum Erase {}
66+
let _: *const Erase = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
67+
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase>()); // Ok, reverse type erasure
68+
69+
struct Erase2(
70+
[u8; 0],
71+
core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
72+
);
73+
let _: *const Erase2 = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
74+
let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase2>()); // Ok, reverse type erasure
75+
76+
let _: *const () = transmute(value::<&&[u8]>()); // Ok, type erasure
77+
let _: &&[u8] = transmute(value::<*const ()>()); // Ok, reverse type erasure
78+
79+
let _: *mut c_void = transmute(value::<&mut &[u8]>()); // Ok, type erasure
80+
let _: &mut &[u8] = transmute(value::<*mut c_void>()); // Ok, reverse type erasure
81+
82+
let _: [u8; size_of::<&[u8]>()] = transmute(value::<&[u8]>()); // Ok, transmute to byte array
83+
let _: &[u8] = transmute(value::<[u8; size_of::<&[u8]>()]>()); // Ok, transmute from byte array
84+
85+
let _: [usize; 2] = transmute(value::<&[u8]>()); // Ok, transmute to int array
86+
let _: &[u8] = transmute(value::<[usize; 2]>()); // Ok, transmute from int array
87+
88+
let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok
89+
let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok
4790
}
4891
}

0 commit comments

Comments
 (0)