diff options
| author | Ralf Jung <post@ralfj.de> | 2025-04-25 10:43:50 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-25 10:43:50 +0000 |
| commit | ea9037e1654d2fb7e29dca87679d3c18a713f82d (patch) | |
| tree | b304db669fcd8cadd9f7d0e3119e54545326318a /compiler/rustc_mir_transform/src | |
| parent | 0ee8f32dcd42a5091579f11db87f86672971a1d1 (diff) | |
| parent | 673c879abb7f3b05e25f0da5654e2a484abc17c0 (diff) | |
| download | rust-ea9037e1654d2fb7e29dca87679d3c18a713f82d.tar.gz rust-ea9037e1654d2fb7e29dca87679d3c18a713f82d.zip | |
Merge pull request #4292 from rust-lang/rustup-2025-04-25
Automatic Rustup
Diffstat (limited to 'compiler/rustc_mir_transform/src')
| -rw-r--r-- | compiler/rustc_mir_transform/src/check_alignment.rs | 32 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs | 100 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/errors.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/lib.rs | 2 |
4 files changed, 150 insertions, 4 deletions
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index b70cca14840..5115583f37c 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -1,3 +1,4 @@ +use rustc_abi::Align; use rustc_index::IndexVec; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::PlaceContext; @@ -11,10 +12,6 @@ pub(super) struct CheckAlignment; impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { fn is_enabled(&self, sess: &Session) -> bool { - // FIXME(#112480) MSVC and rustc disagree on minimum stack alignment on x86 Windows - if sess.target.llvm_target == "i686-pc-windows-msvc" { - return false; - } sess.ub_checks() } @@ -87,6 +84,33 @@ fn insert_alignment_check<'tcx>( ))), }); + // If this target does not have reliable alignment, further limit the mask by anding it with + // the mask for the highest reliable alignment. + #[allow(irrefutable_let_patterns)] + if let max_align = tcx.sess.target.max_reliable_alignment() + && max_align < Align::MAX + { + let max_mask = max_align.bytes() - 1; + let max_mask = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::Val( + ConstValue::Scalar(Scalar::from_target_usize(max_mask, &tcx)), + tcx.types.usize, + ), + })); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + alignment_mask, + Rvalue::BinaryOp( + BinOp::BitAnd, + Box::new((Operand::Copy(alignment_mask), max_mask)), + ), + ))), + }); + } + // BitAnd the alignment mask with the pointer let alignment_bits = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); diff --git a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs new file mode 100644 index 00000000000..8be782dcbf0 --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs @@ -0,0 +1,100 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; +use rustc_middle::ty::*; +use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES; +use rustc_span::source_map::Spanned; +use rustc_span::{Span, sym}; + +use crate::errors::UnnecessaryTransmute as Error; + +/// Check for transmutes that overlap with stdlib methods. +/// For example, transmuting `[u8; 4]` to `u32`. +pub(super) struct CheckUnnecessaryTransmutes; + +impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let mut checker = UnnecessaryTransmuteChecker { body, tcx }; + checker.visit_body(body); + } +} + +struct UnnecessaryTransmuteChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> { + fn is_unnecessary_transmute( + &self, + function: &Operand<'tcx>, + arg: String, + span: Span, + ) -> Option<Error> { + 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_be_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_be_bytes` if you expect a particular byte order", + ), + span, + }, + // char → u32 + (Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")), + // 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 UnnecessaryTransmuteChecker<'_, '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_unnecessary_transmute( + func, + self.tcx.sess.source_map().span_to_snippet(arg).expect("ok"), + span, + ) + && let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes) + { + self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint); + } + } +} diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 29698b0c2e4..5b03a4987ed 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 UnnecessaryTransmute { + pub span: Span, + pub sugg: String, + pub help: Option<&'static str>, +} + +// Needed for def_path_str +impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::mir_transform_unnecessary_transmute); + diag.span_suggestion( + self.span, + "replace this with", + 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 4d74ecddfb0..6977d23bd0e 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_unnecessary_transmutes: CheckUnnecessaryTransmutes; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck : CleanupPostBorrowck; @@ -391,6 +392,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> { &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), &Lint(check_undefined_transmutes::CheckUndefinedTransmutes), + &Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, &Lint(sanity_check::SanityCheck), |
