diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 10b85be2cfbe1..5b181c7ea48c1 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -84,6 +84,7 @@ declare_lint_pass! { PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, REDUNDANT_LIFETIMES, + REDUNDANT_TRANSMUTATION, REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, @@ -4980,6 +4981,28 @@ declare_lint! { "detects pointer to integer transmutes in const functions and associated constants", } +declare_lint! { + /// The `redundant_transmutation` lint detects transmutations that have safer alternatives. + /// + /// ### Example + /// + /// ``` + /// fn bytes_at_home(x: [u8; 4]) -> u32 { + /// transmute(x) + /// } + /// ``` + /// + /// {{produces}} + /// + /// ## Explanation + /// + /// People dont realize that safer methods such as [`u32::to_ne_bytes`] exist, + /// so this lint exists to lint on cases where people write transmutes that dont need to be there. + pub REDUNDANT_TRANSMUTATION, + Warn, + "detects transmutes that are shadowed by std methods" +} + declare_lint! { /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, /// that runs a custom `Drop` destructor. diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index 5628f4c9381b3..693664e5d6065 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -42,6 +42,8 @@ mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspen .help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point mir_transform_operation_will_panic = this operation will panic at runtime +mir_transform_redundant_transmute = this transmute could be performed safely + mir_transform_tail_expr_drop_order = relative drop order changing in Rust 2024 .temporaries = in Rust 2024, this temporary value will be dropped first .observers = in Rust 2024, this local variable or temporary value will be dropped second diff --git a/compiler/rustc_mir_transform/src/check_redundant_transmutes.rs b/compiler/rustc_mir_transform/src/check_redundant_transmutes.rs new file mode 100644 index 0000000000000..d665cf2d1f97d --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_redundant_transmutes.rs @@ -0,0 +1,103 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; +use rustc_middle::ty::{TyCtxt, UintTy}; +use rustc_session::lint::builtin::REDUNDANT_TRANSMUTATION; +use rustc_span::source_map::Spanned; +use rustc_span::{Span, sym}; +use rustc_type_ir::TyKind::*; + +use crate::errors::RedundantTransmute as Error; + +/// Check for transmutes that overlap with stdlib methods. +/// For example, transmuting `[u8; 4]` to `u32`. +pub(super) struct CheckRedundantTransmutes; + +impl<'tcx> crate::MirLint<'tcx> for CheckRedundantTransmutes { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let mut checker = RedundantTransmutesChecker { body, tcx }; + checker.visit_body(body); + } +} + +struct RedundantTransmutesChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> RedundantTransmutesChecker<'a, 'tcx> { + fn is_redundant_transmute( + &self, + function: &Operand<'tcx>, + arg: String, + span: Span, + ) -> Option { + let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); + let [input] = fn_sig.inputs() else { return None }; + + let err = |sugg| Error { span, sugg, help: None }; + + Some(match (input.kind(), fn_sig.output().kind()) { + // dont check the length; transmute does that for us. + // [u8; _] => primitive + (Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()), + help: Some( + "there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order", + ), + span, + }, + // primitive => [u8; _] + (Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{input}::to_ne_bytes({arg})"), + help: Some( + "there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order", + ), + span, + }, + // char → u32 + (Char, Uint(UintTy::U32)) => err(format!("({arg}) as u32")), + // u32 → char + (Uint(UintTy::U32), Char) => Error { + sugg: format!("char::from_u32_unchecked({arg})"), + help: Some("consider `char::from_u32(…).unwrap()`"), + span, + }, + // uNN → iNN + (Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())), + // iNN → uNN + (Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())), + // fNN → uNN + (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())), + // uNN → fNN + (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())), + // bool → { x8 } + (Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())), + // u8 → bool + (Uint(_), Bool) => err(format!("({arg} == 1)")), + _ => return None, + }) + } +} + +impl<'tcx> Visitor<'tcx> for RedundantTransmutesChecker<'_, 'tcx> { + // Check each block's terminator for calls to pointer to integer transmutes + // in const functions or associated constants and emit a lint. + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + if let TerminatorKind::Call { func, args, .. } = &terminator.kind + && let [Spanned { span: arg, .. }] = **args + && let Some((func_def_id, _)) = func.const_fn_def() + && self.tcx.is_intrinsic(func_def_id, sym::transmute) + && let span = self.body.source_info(location).span + && let Some(lint) = self.is_redundant_transmute( + func, + self.tcx.sess.source_map().span_to_snippet(arg).expect("ok"), + span, + ) + && let Some(call_id) = self.body.source.def_id().as_local() + { + let hir_id = self.tcx.local_def_id_to_hir_id(call_id); + + self.tcx.emit_node_span_lint(REDUNDANT_TRANSMUTATION, hir_id, span, lint); + } + } +} diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 29698b0c2e445..a4a5615f54605 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -158,6 +158,26 @@ pub(crate) struct MustNotSuspendReason { pub reason: String, } +pub(crate) struct RedundantTransmute { + pub span: Span, + pub sugg: String, + pub help: Option<&'static str>, +} + +// Needed for def_path_str +impl<'a> LintDiagnostic<'a, ()> for RedundantTransmute { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::mir_transform_redundant_transmute); + diag.span_suggestion( + self.span, + "replace `transmute`", + self.sugg, + lint::Applicability::MachineApplicable, + ); + self.help.map(|help| diag.help(help)); + } +} + #[derive(LintDiagnostic)] #[diag(mir_transform_undefined_transmute)] #[note] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 5df12ac4d8bc2..98cd3680f47d8 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -125,6 +125,7 @@ declare_passes! { mod check_null : CheckNull; mod check_packed_ref : CheckPackedRef; mod check_undefined_transmutes : CheckUndefinedTransmutes; + mod check_redundant_transmutes: CheckRedundantTransmutes; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck : CleanupPostBorrowck; @@ -387,6 +388,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), &Lint(check_undefined_transmutes::CheckUndefinedTransmutes), + &Lint(check_redundant_transmutes::CheckRedundantTransmutes), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, &Lint(sanity_check::SanityCheck), diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 73ab4f1e52ade..894f2614ba773 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -21,6 +21,7 @@ pub(super) const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. #[inline] #[must_use] +#[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { @@ -229,6 +230,7 @@ impl FromStr for char { } #[inline] +#[cfg_attr(not(bootstrap), allow(redundant_transmutation))] const fn char_try_from_u32(i: u32) -> Result { // This is an optimized version of the check // (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF), diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 5e45974b3d422..36ca521726562 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -901,6 +901,7 @@ impl f128 { #[inline] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub const fn to_bits(self) -> u128 { // SAFETY: `u128` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -948,6 +949,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub const fn from_bits(v: u128) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u128` is a plain old datatype so we can always transmute from it. diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e3176cd168852..2002bd7decf0c 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -889,6 +889,7 @@ impl f16 { #[inline] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub const fn to_bits(self) -> u16 { // SAFETY: `u16` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -935,6 +936,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub const fn from_bits(v: u16) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u16` is a plain old datatype so we can always transmute from it. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index de1557ccc9028..67ea585cd124e 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -708,8 +708,7 @@ impl f32 { pub const fn is_sign_negative(self) -> bool { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & 0x8000_0000 != 0 } + self.to_bits() & 0x8000_0000 != 0 } /// Returns the least number greater than `self`. @@ -1093,6 +1092,7 @@ impl f32 { #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[inline] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub const fn to_bits(self) -> u32 { // SAFETY: `u32` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -1138,6 +1138,7 @@ impl f32 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub const fn from_bits(v: u32) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u32` is a plain old datatype so we can always transmute from it. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 65b5f3b9af093..6dcf7cb1365ca 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -716,8 +716,7 @@ impl f64 { pub const fn is_sign_negative(self) -> bool { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & Self::SIGN_MASK != 0 } + self.to_bits() & Self::SIGN_MASK != 0 } #[must_use] @@ -1092,6 +1091,7 @@ impl f64 { without modifying the original"] #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] #[inline] pub const fn to_bits(self) -> u64 { // SAFETY: `u64` is a plain old datatype so we can always transmute to it. @@ -1138,6 +1138,7 @@ impl f64 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] pub const fn from_bits(v: u64) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u64` is a plain old datatype so we can always transmute from it. diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 7d99aaa173143..c2bf675eeb86a 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3678,6 +3678,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[must_use = "this returns the result of the operation, \ @@ -3781,6 +3782,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 405c71121caad..1790ffd48cb17 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -3518,6 +3518,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[inline] @@ -3619,6 +3620,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(redundant_transmutation))] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs index 9187de5636202..ddf4031e80d78 100644 --- a/tests/ui/consts/const-eval/raw-bytes.rs +++ b/tests/ui/consts/const-eval/raw-bytes.rs @@ -2,7 +2,7 @@ //@ ignore-endian-big // ignore-tidy-linelength //@ normalize-stderr: "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼" -> "╾ALLOC_ID$1╼" -#![allow(invalid_value)] +#![allow(invalid_value, redundant_transmutation)] #![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] use std::mem; diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.rs b/tests/ui/consts/const-eval/transmute-const-promotion.rs index 1f0240d4b5ac7..d2973734063e2 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.rs +++ b/tests/ui/consts/const-eval/transmute-const-promotion.rs @@ -1,3 +1,4 @@ +#![allow(redundant_transmutation)] use std::mem; fn main() { diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.stderr b/tests/ui/consts/const-eval/transmute-const-promotion.stderr index 3603db03bb204..eb2fed091c34a 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.stderr +++ b/tests/ui/consts/const-eval/transmute-const-promotion.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/transmute-const-promotion.rs:4:37 + --> $DIR/transmute-const-promotion.rs:5:37 | LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) }; | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use diff --git a/tests/ui/consts/const-eval/transmute-const.rs b/tests/ui/consts/const-eval/transmute-const.rs index 1cfad00ca76df..91029c0fe4409 100644 --- a/tests/ui/consts/const-eval/transmute-const.rs +++ b/tests/ui/consts/const-eval/transmute-const.rs @@ -1,3 +1,4 @@ +#![allow(redundant_transmutation)] use std::mem; static FOO: bool = unsafe { mem::transmute(3u8) }; diff --git a/tests/ui/consts/const-eval/transmute-const.stderr b/tests/ui/consts/const-eval/transmute-const.stderr index d72289487d7bf..35a5cabaa6710 100644 --- a/tests/ui/consts/const-eval/transmute-const.stderr +++ b/tests/ui/consts/const-eval/transmute-const.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/transmute-const.rs:3:1 + --> $DIR/transmute-const.rs:4:1 | LL | static FOO: bool = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 5be444e667a1f..9efb6977e9a78 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -3,7 +3,7 @@ //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "0x0+" -> "0x0" #![feature(never_type)] -#![allow(invalid_value)] +#![allow(invalid_value, redundant_transmutation)] use std::mem; diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.rs b/tests/ui/consts/const-eval/ub-wide-ptr.rs index a071a44272b4d..9c6e2328c76e9 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.rs +++ b/tests/ui/consts/const-eval/ub-wide-ptr.rs @@ -1,5 +1,5 @@ // ignore-tidy-linelength -#![allow(unused)] +#![allow(unused, redundant_transmutation)] #![feature(ptr_metadata)] use std::{ptr, mem}; diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs index d2b157e03e7cb..b4575f2b03d93 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -2,6 +2,7 @@ //@ [no_flag] check-pass //@ [with_flag] compile-flags: -Zextra-const-ub-checks #![feature(never_type)] +#![allow(redundant_transmutation)] use std::mem::transmute; use std::ptr::addr_of; diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr index 0100aafb6b7c6..ea3b0e70b8285 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:29:20 + --> $DIR/detect-extra-ub.rs:30:20 | LL | let _x: bool = transmute(3u8); | ^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:35:21 + --> $DIR/detect-extra-ub.rs:36:21 | LL | let _x: usize = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a pointer, but expected an integer @@ -14,7 +14,7 @@ LL | let _x: usize = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:41:28 + --> $DIR/detect-extra-ub.rs:42:28 | LL | let _x: PtrSizedEnum = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value at .: encountered a pointer, but expected an integer @@ -23,7 +23,7 @@ LL | let _x: PtrSizedEnum = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:48:30 + --> $DIR/detect-extra-ub.rs:49:30 | LL | let _x: (usize, usize) = transmute(x); | ^^^^^^^^^^^^ constructing invalid value at .0: encountered a pointer, but expected an integer @@ -32,19 +32,19 @@ LL | let _x: (usize, usize) = transmute(x); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:54:20 + --> $DIR/detect-extra-ub.rs:55:20 | LL | let _x: &u32 = transmute(&[0u8; 4]); | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 4 byte alignment but found 1) error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:62:13 + --> $DIR/detect-extra-ub.rs:63:13 | LL | let v = *addr_of!(data).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:82:16 + --> $DIR/detect-extra-ub.rs:83:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a partial pointer or a mix of pointers @@ -53,7 +53,7 @@ LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:97:16 + --> $DIR/detect-extra-ub.rs:98:16 | LL | let _val = &*slice; | ^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object diff --git a/tests/ui/consts/issue-69532.rs b/tests/ui/consts/issue-69532.rs index 285cfe7213bae..43ab1d6cca743 100644 --- a/tests/ui/consts/issue-69532.rs +++ b/tests/ui/consts/issue-69532.rs @@ -1,8 +1,8 @@ //@ run-pass const fn make_nans() -> (f64, f64, f32, f32) { - let nan1: f64 = unsafe { std::mem::transmute(0x7FF0_0001_0000_0001u64) }; - let nan2: f64 = unsafe { std::mem::transmute(0x7FF0_0000_0000_0001u64) }; + let nan1 = f64::from_bits(0x7FF0_0001_0000_0001); + let nan2 = f64::from_bits(0x7FF0_0000_0000_0001); let nan1_32 = nan1 as f32; let nan2_32 = nan2 as f32; diff --git a/tests/ui/issues/issue-25746-bool-transmute.rs b/tests/ui/issues/issue-25746-bool-transmute.rs index f8cdc980daa48..36967942dd2e9 100644 --- a/tests/ui/issues/issue-25746-bool-transmute.rs +++ b/tests/ui/issues/issue-25746-bool-transmute.rs @@ -1,4 +1,5 @@ //@ run-pass +#![allow(redundant_transmutation)] use std::mem::transmute; fn main() { diff --git a/tests/ui/transmute/redundant-transmutation.fixed b/tests/ui/transmute/redundant-transmutation.fixed new file mode 100644 index 0000000000000..b703caf45c9f2 --- /dev/null +++ b/tests/ui/transmute/redundant-transmutation.fixed @@ -0,0 +1,85 @@ +//@ run-rustfix +#![deny(redundant_transmutation)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { u32::to_ne_bytes(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = u16::from_ne_bytes(*b"01"); + //~^ ERROR + let x: [u8; 2] = u16::to_ne_bytes(x); + //~^ ERROR + let x: u32 = u32::from_ne_bytes(*b"0123"); + //~^ ERROR + let x: [u8; 4] = u32::to_ne_bytes(x); + //~^ ERROR + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = u64::to_ne_bytes(x); + //~^ ERROR + + let y: i16 = i16::from_ne_bytes(*b"01"); + //~^ ERROR + let y: [u8; 2] = i16::to_ne_bytes(y); + //~^ ERROR + let y: i32 = i32::from_ne_bytes(*b"0123"); + //~^ ERROR + let y: [u8; 4] = i32::to_ne_bytes(y); + //~^ ERROR + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = i64::to_ne_bytes(y); + //~^ ERROR + + let z: f32 = f32::from_ne_bytes(*b"0123"); + //~^ ERROR + let z: [u8; 4] = f32::to_ne_bytes(z); + //~^ ERROR + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = f64::to_ne_bytes(z); + //~^ ERROR + + let y: u32 = ('🦀') as u32; + //~^ ERROR + let y: char = char::from_u32_unchecked(y); + //~^ ERROR + + let x: u16 = i16::cast_unsigned(8i16); + //~^ ERROR + let x: i16 = u16::cast_signed(x); + //~^ ERROR + let x: u32 = i32::cast_unsigned(4i32); + //~^ ERROR + let x: i32 = u32::cast_signed(x); + //~^ ERROR + let x: u64 = i64::cast_unsigned(7i64); + //~^ ERROR + let x: i64 = u64::cast_signed(x); + //~^ ERROR + + let y: f32 = f32::from_bits(1u32); + //~^ ERROR + let y: u32 = f32::to_bits(y); + //~^ ERROR + let y: f64 = f64::from_bits(3u64); + //~^ ERROR + let y: u64 = f64::to_bits(2.0); + //~^ ERROR + + let z: bool = (1u8 == 1); + //~^ ERROR + let z: u8 = (z) as u8; + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = (z) as i8; + //~^ ERROR + } +} diff --git a/tests/ui/transmute/redundant-transmutation.rs b/tests/ui/transmute/redundant-transmutation.rs new file mode 100644 index 0000000000000..9062131572c3e --- /dev/null +++ b/tests/ui/transmute/redundant-transmutation.rs @@ -0,0 +1,85 @@ +//@ run-rustfix +#![deny(redundant_transmutation)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { transmute(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = transmute(*b"01"); + //~^ ERROR + let x: [u8; 2] = transmute(x); + //~^ ERROR + let x: u32 = transmute(*b"0123"); + //~^ ERROR + let x: [u8; 4] = transmute(x); + //~^ ERROR + let x: u64 = transmute(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = transmute(x); + //~^ ERROR + + let y: i16 = transmute(*b"01"); + //~^ ERROR + let y: [u8; 2] = transmute(y); + //~^ ERROR + let y: i32 = transmute(*b"0123"); + //~^ ERROR + let y: [u8; 4] = transmute(y); + //~^ ERROR + let y: i64 = transmute(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = transmute(y); + //~^ ERROR + + let z: f32 = transmute(*b"0123"); + //~^ ERROR + let z: [u8; 4] = transmute(z); + //~^ ERROR + let z: f64 = transmute(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = transmute(z); + //~^ ERROR + + let y: u32 = transmute('🦀'); + //~^ ERROR + let y: char = transmute(y); + //~^ ERROR + + let x: u16 = transmute(8i16); + //~^ ERROR + let x: i16 = transmute(x); + //~^ ERROR + let x: u32 = transmute(4i32); + //~^ ERROR + let x: i32 = transmute(x); + //~^ ERROR + let x: u64 = transmute(7i64); + //~^ ERROR + let x: i64 = transmute(x); + //~^ ERROR + + let y: f32 = transmute(1u32); + //~^ ERROR + let y: u32 = transmute(y); + //~^ ERROR + let y: f64 = transmute(3u64); + //~^ ERROR + let y: u64 = transmute(2.0); + //~^ ERROR + + let z: bool = transmute(1u8); + //~^ ERROR + let z: u8 = transmute(z); + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = transmute(z); + //~^ ERROR + } +} diff --git a/tests/ui/transmute/redundant-transmutation.stderr b/tests/ui/transmute/redundant-transmutation.stderr new file mode 100644 index 0000000000000..44e803679f672 --- /dev/null +++ b/tests/ui/transmute/redundant-transmutation.stderr @@ -0,0 +1,235 @@ +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:7:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ help: replace `transmute`: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order +note: the lint level is defined here + --> $DIR/redundant-transmutation.rs:2:9 + | +LL | #![deny(redundant_transmutation)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:13:22 + | +LL | let x: u16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace `transmute`: `u16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:15:26 + | +LL | let x: [u8; 2] = transmute(x); + | ^^^^^^^^^^^^ help: replace `transmute`: `u16::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:17:22 + | +LL | let x: u32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace `transmute`: `u32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:19:26 + | +LL | let x: [u8; 4] = transmute(x); + | ^^^^^^^^^^^^ help: replace `transmute`: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:21:22 + | +LL | let x: u64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace `transmute`: `u64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:23:26 + | +LL | let x: [u8; 8] = transmute(x); + | ^^^^^^^^^^^^ help: replace `transmute`: `u64::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:26:22 + | +LL | let y: i16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace `transmute`: `i16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:28:26 + | +LL | let y: [u8; 2] = transmute(y); + | ^^^^^^^^^^^^ help: replace `transmute`: `i16::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:30:22 + | +LL | let y: i32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace `transmute`: `i32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:32:26 + | +LL | let y: [u8; 4] = transmute(y); + | ^^^^^^^^^^^^ help: replace `transmute`: `i32::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:34:22 + | +LL | let y: i64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace `transmute`: `i64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:36:26 + | +LL | let y: [u8; 8] = transmute(y); + | ^^^^^^^^^^^^ help: replace `transmute`: `i64::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:39:22 + | +LL | let z: f32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace `transmute`: `f32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:41:26 + | +LL | let z: [u8; 4] = transmute(z); + | ^^^^^^^^^^^^ help: replace `transmute`: `f32::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:43:22 + | +LL | let z: f64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace `transmute`: `f64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:45:26 + | +LL | let z: [u8; 8] = transmute(z); + | ^^^^^^^^^^^^ help: replace `transmute`: `f64::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_ne_bytes` if you expect a particular byte order + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:48:22 + | +LL | let y: u32 = transmute('🦀'); + | ^^^^^^^^^^^^^^^ help: replace `transmute`: `('🦀') as u32` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:50:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ help: replace `transmute`: `char::from_u32_unchecked(y)` + | + = help: consider `char::from_u32(…).unwrap()` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:53:22 + | +LL | let x: u16 = transmute(8i16); + | ^^^^^^^^^^^^^^^ help: replace `transmute`: `i16::cast_unsigned(8i16)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:55:22 + | +LL | let x: i16 = transmute(x); + | ^^^^^^^^^^^^ help: replace `transmute`: `u16::cast_signed(x)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:57:22 + | +LL | let x: u32 = transmute(4i32); + | ^^^^^^^^^^^^^^^ help: replace `transmute`: `i32::cast_unsigned(4i32)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:59:22 + | +LL | let x: i32 = transmute(x); + | ^^^^^^^^^^^^ help: replace `transmute`: `u32::cast_signed(x)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:61:22 + | +LL | let x: u64 = transmute(7i64); + | ^^^^^^^^^^^^^^^ help: replace `transmute`: `i64::cast_unsigned(7i64)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:63:22 + | +LL | let x: i64 = transmute(x); + | ^^^^^^^^^^^^ help: replace `transmute`: `u64::cast_signed(x)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:66:22 + | +LL | let y: f32 = transmute(1u32); + | ^^^^^^^^^^^^^^^ help: replace `transmute`: `f32::from_bits(1u32)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:68:22 + | +LL | let y: u32 = transmute(y); + | ^^^^^^^^^^^^ help: replace `transmute`: `f32::to_bits(y)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:70:22 + | +LL | let y: f64 = transmute(3u64); + | ^^^^^^^^^^^^^^^ help: replace `transmute`: `f64::from_bits(3u64)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:72:22 + | +LL | let y: u64 = transmute(2.0); + | ^^^^^^^^^^^^^^ help: replace `transmute`: `f64::to_bits(2.0)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:75:23 + | +LL | let z: bool = transmute(1u8); + | ^^^^^^^^^^^^^^ help: replace `transmute`: `(1u8 == 1)` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:77:21 + | +LL | let z: u8 = transmute(z); + | ^^^^^^^^^^^^ help: replace `transmute`: `(z) as u8` + +error: this transmute could be performed safely + --> $DIR/redundant-transmutation.rs:82:21 + | +LL | let z: i8 = transmute(z); + | ^^^^^^^^^^^^ help: replace `transmute`: `(z) as i8` + +error: aborting due to 32 previous errors +