Skip to content

Commit 8134863

Browse files
Fix behavior with generic lifetime parameters
1 parent 96b11a5 commit 8134863

File tree

3 files changed

+88
-14
lines changed

3 files changed

+88
-14
lines changed

clippy_lints/src/transmute.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use rustc::lint::*;
2-
use rustc::ty::{self, Ty};
2+
use rustc::ty::{self, Ty, walk::TypeWalker};
33
use rustc::hir::*;
44
use std::borrow::Cow;
5+
use std::mem;
56
use syntax::ast;
67
use utils::{last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then};
78
use utils::{opt_def_id, sugg};
@@ -363,8 +364,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
363364
}
364365
)
365366
} else {
366-
// In this case they differ only in lifetime
367-
if ty_from != ty_to {
367+
if !differ_only_in_lifetime_params(from_ty, to_ty) {
368368
span_lint_and_then(
369369
cx,
370370
TRANSMUTE_PTR_TO_PTR,
@@ -448,6 +448,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
448448
}
449449
}
450450

451+
/// Returns true if `type1` and `type2` are the same type except for their lifetime parameters
452+
fn differ_only_in_lifetime_params(type1: Ty, type2: Ty) -> bool {
453+
use rustc::ty::TypeVariants::*;
454+
if TypeWalker::new(type1).count() != TypeWalker::new(type2).count() {
455+
return false;
456+
}
457+
TypeWalker::new(type1)
458+
.zip(TypeWalker::new(type2))
459+
.all(|(t1, t2)| {
460+
match (&t1.sty, &t2.sty) {
461+
// types with generic parameters which can contain lifetimes
462+
(TyAdt(_, sub1), TyAdt(_, sub2))
463+
| (TyFnDef(_, sub1), TyFnDef(_, sub2))
464+
| (TyAnon(_, sub1), TyAnon(_, sub2))
465+
=> {
466+
// Iterate over generic parameters, which are either Lifetimes or Types.
467+
// Here we only need to check that they are the same type of thing, because
468+
// if they are both Lifetimes then we don't care about their equality, and if
469+
// they are both Types, we will check their equality later in the type walk.
470+
sub1.iter().count() == sub2.iter().count()
471+
&& sub1.iter().zip(sub2.iter()).all(|(k1, k2)| {
472+
mem::discriminant(&k1.unpack()) == mem::discriminant(&k2.unpack())
473+
})
474+
}
475+
// types without subtypes: check that the types are equal
476+
(TyBool, TyBool)
477+
| (TyChar, TyChar)
478+
| (TyInt(_), TyInt(_))
479+
| (TyUint(_), TyUint(_))
480+
| (TyFloat(_), TyFloat(_))
481+
| (TyForeign(_), TyForeign(_))
482+
| (TyStr, TyStr)
483+
| (TyNever, TyNever)
484+
| (TyInfer(_), TyInfer(_))
485+
=> t1.sty == t2.sty,
486+
// types with subtypes: return true for now if they are the same sort of type.
487+
// we will check their subtypes later
488+
(sty1, sty2) => mem::discriminant(sty1) == mem::discriminant(sty2)
489+
}
490+
})
491+
}
492+
451493
/// Get the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
452494
/// not available , use
453495
/// the type's `ToString` implementation. In weird cases it could lead to types

tests/ui/transmute.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T {
155155
std::mem::transmute::<&'a T, &'b T>(t)
156156
}
157157

158+
struct LifetimeParam<'a> {
159+
s: &'a str,
160+
}
161+
162+
struct GenericParam<T> {
163+
t: T,
164+
}
165+
158166
#[warn(transmute_ptr_to_ptr)]
159167
fn transmute_ptr_to_ptr() {
160168
let ptr = &1u32 as *const u32;
@@ -165,15 +173,27 @@ fn transmute_ptr_to_ptr() {
165173
let _: *mut f32 = std::mem::transmute(mut_ptr);
166174
// ref-ref transmutes; bad
167175
let _: &f32 = std::mem::transmute(&1u32);
176+
let _: &f64 = std::mem::transmute(&1f32);
177+
// ^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
178+
// the same type
168179
let _: &mut f32 = std::mem::transmute(&mut 1u32);
180+
let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
169181
}
170-
// These should be fine:
171-
// Recommendations for solving the above; if these break we need to update
182+
183+
// these are recommendations for solving the above; if these lint we need to update
172184
// those suggestions
173185
let _ = ptr as *const f32;
174186
let _ = mut_ptr as *mut f32;
175187
let _ = unsafe { &*(&1u32 as *const u32 as *const f32) };
176188
let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) };
189+
190+
// transmute internal lifetimes, should not lint
191+
let s = "hello world".to_owned();
192+
let lp = LifetimeParam { s: &s };
193+
let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) };
194+
let _: &GenericParam<&LifetimeParam<'static>> = unsafe {
195+
std::mem::transmute(&GenericParam { t: &lp})
196+
};
177197
}
178198

179199
fn main() { }

tests/ui/transmute.stderr

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,30 +205,42 @@ error: transmute from a `&mut [u8]` to a `&mut str`
205205
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
206206

207207
error: transmute from a pointer to a pointer
208-
--> $DIR/transmute.rs:164:29
208+
--> $DIR/transmute.rs:172:29
209209
|
210-
164 | let _: *const f32 = std::mem::transmute(ptr);
210+
172 | let _: *const f32 = std::mem::transmute(ptr);
211211
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32`
212212
|
213213
= note: `-D transmute-ptr-to-ptr` implied by `-D warnings`
214214

215215
error: transmute from a pointer to a pointer
216-
--> $DIR/transmute.rs:165:27
216+
--> $DIR/transmute.rs:173:27
217217
|
218-
165 | let _: *mut f32 = std::mem::transmute(mut_ptr);
218+
173 | let _: *mut f32 = std::mem::transmute(mut_ptr);
219219
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
220220

221221
error: transmute from a reference to a reference
222-
--> $DIR/transmute.rs:167:23
222+
--> $DIR/transmute.rs:175:23
223223
|
224-
167 | let _: &f32 = std::mem::transmute(&1u32);
224+
175 | let _: &f32 = std::mem::transmute(&1u32);
225225
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
226226

227227
error: transmute from a reference to a reference
228-
--> $DIR/transmute.rs:168:27
228+
--> $DIR/transmute.rs:176:23
229229
|
230-
168 | let _: &mut f32 = std::mem::transmute(&mut 1u32);
230+
176 | let _: &f64 = std::mem::transmute(&1f32);
231+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)`
232+
233+
error: transmute from a reference to a reference
234+
--> $DIR/transmute.rs:179:27
235+
|
236+
179 | let _: &mut f32 = std::mem::transmute(&mut 1u32);
231237
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
232238

233-
error: aborting due to 36 previous errors
239+
error: transmute from a reference to a reference
240+
--> $DIR/transmute.rs:180:37
241+
|
242+
180 | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
243+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
244+
245+
error: aborting due to 38 previous errors
234246

0 commit comments

Comments
 (0)