Skip to content

Commit

Permalink
Don't lint transmute_undefined_repr when changing the type of gener…
Browse files Browse the repository at this point in the history
…ic params
  • Loading branch information
Jarcho committed Mar 16, 2022
1 parent d23ddab commit 8e5208c
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 28 deletions.
64 changes: 45 additions & 19 deletions clippy_lints/src/transmute/transmute_undefined_repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_c_void;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
use rustc_span::Span;

Expand Down Expand Up @@ -127,6 +127,17 @@ pub(super) fn check<'tcx>(
} => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
(ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
= (from_ty.kind(), to_ty.kind())
&& from_def == to_def
{
if same_except_params(from_subs, to_subs) {
return false;
}
Some(from_def.did())
} else {
None
};
span_lint_and_then(
cx,
TRANSMUTE_UNDEFINED_REPR,
Expand All @@ -136,21 +147,17 @@ pub(super) fn check<'tcx>(
from_ty_orig, to_ty_orig
),
|diag| {
if_chain! {
if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def());
if from_def == to_def;
then {
diag.note(&format!(
"two instances of the same generic type (`{}`) may have different layouts",
cx.tcx.item_name(from_def.did())
));
} else {
if from_ty_orig.peel_refs() != from_ty {
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
}
if to_ty_orig.peel_refs() != to_ty {
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
}
if let Some(same_adt_did) = same_adt_did {
diag.note(&format!(
"two instances of the same generic type (`{}`) may have different layouts",
cx.tcx.item_name(same_adt_did)
));
} else {
if from_ty_orig.peel_refs() != from_ty {
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
}
if to_ty_orig.peel_refs() != to_ty {
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
}
}
},
Expand Down Expand Up @@ -197,10 +204,13 @@ pub(super) fn check<'tcx>(
continue;
},
(
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
)
| (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
| (
ReducedTy::UnorderedFields(_) | ReducedTy::Param,
ReducedTy::UnorderedFields(_) | ReducedTy::Param,
) => break,
},
}
}
Expand Down Expand Up @@ -264,6 +274,8 @@ enum ReducedTy<'tcx> {
UnorderedFields(Ty<'tcx>),
/// The type is a reference to the contained type.
Ref(Ty<'tcx>),
/// The type is a generic parameter.
Param,
/// Any other type.
Other(Ty<'tcx>),
}
Expand Down Expand Up @@ -317,6 +329,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
ty::Foreign(_) => ReducedTy::TypeErasure,
ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
ty::Param(_) => ReducedTy::Param,
_ => ReducedTy::Other(ty),
};
}
Expand Down Expand Up @@ -344,3 +357,16 @@ fn is_size_pair(ty: Ty<'_>) -> bool {
false
}
}

fn same_except_params(subs1: SubstsRef<'_>, subs2: SubstsRef<'_>) -> bool {
// TODO: check const parameters as well. Currently this will consider `Array<5>` the same as
// `Array<6>`
for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) {
match (ty1.kind(), ty2.kind()) {
(ty::Param(_), _) | (_, ty::Param(_)) => (),
(ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (),
_ => return false,
}
}
true
}
32 changes: 32 additions & 0 deletions tests/ui/transmute_undefined_repr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![warn(clippy::transmute_undefined_repr)]
#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)]

use core::any::TypeId;
use core::ffi::c_void;
use core::mem::{size_of, transmute, MaybeUninit};

Expand Down Expand Up @@ -110,3 +111,34 @@ fn main() {
let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
}
}

fn _with_generics<T: 'static, U: 'static>() {
if TypeId::of::<T>() != TypeId::of::<u32>() || TypeId::of::<T>() != TypeId::of::<U>() {
return;
}
unsafe {
let _: &u32 = transmute(value::<&T>()); // Ok
let _: &T = transmute(value::<&u32>()); // Ok

let _: Vec<U> = transmute(value::<Vec<T>>()); // Ok
let _: Vec<T> = transmute(value::<Vec<U>>()); // Ok

let _: Ty<&u32> = transmute(value::<&T>()); // Ok
let _: Ty<&T> = transmute(value::<&u32>()); // Ok

let _: Vec<u32> = transmute(value::<Vec<T>>()); // Ok
let _: Vec<T> = transmute(value::<Vec<u32>>()); // Ok

let _: &Ty2<u32, u32> = transmute(value::<&Ty2<T, U>>()); // Ok
let _: &Ty2<T, U> = transmute(value::<&Ty2<u32, u32>>()); // Ok

let _: Vec<Vec<u32>> = transmute(value::<Vec<Vec<T>>>()); // Ok
let _: Vec<Vec<T>> = transmute(value::<Vec<Vec<u32>>>()); // Ok

let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err

let _: *const u32 = transmute(value::<Box<T>>()); // Ok
let _: Box<T> = transmute(value::<*const u32>()); // Ok
}
}
34 changes: 25 additions & 9 deletions tests/ui/transmute_undefined_repr.stderr
Original file line number Diff line number Diff line change
@@ -1,64 +1,80 @@
error: transmute from `Ty2<u32, i32>` which has an undefined layout
--> $DIR/transmute_undefined_repr.rs:26:33
--> $DIR/transmute_undefined_repr.rs:27:33
|
LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`

error: transmute into `Ty2<u32, i32>` which has an undefined layout
--> $DIR/transmute_undefined_repr.rs:27:32
--> $DIR/transmute_undefined_repr.rs:28:32
|
LL | let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:32:32
--> $DIR/transmute_undefined_repr.rs:33:32
|
LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Ty2`) may have different layouts

error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:33:36
--> $DIR/transmute_undefined_repr.rs:34:36
|
LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Ty2`) may have different layouts

error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:38:33
--> $DIR/transmute_undefined_repr.rs:39:33
|
LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Ty2`) may have different layouts

error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:39:37
--> $DIR/transmute_undefined_repr.rs:40:37
|
LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Ty2`) may have different layouts

error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:56:45
--> $DIR/transmute_undefined_repr.rs:57:45
|
LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Ty2`) may have different layouts

error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:57:37
--> $DIR/transmute_undefined_repr.rs:58:37
|
LL | let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Ty2`) may have different layouts

error: aborting due to 8 previous errors
error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:138:35
|
LL | let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Vec`) may have different layouts

error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout
--> $DIR/transmute_undefined_repr.rs:139:35
|
LL | let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: two instances of the same generic type (`Vec`) may have different layouts

error: aborting due to 10 previous errors

0 comments on commit 8e5208c

Please sign in to comment.