diff options
Diffstat (limited to 'compiler/rustc_mir_transform/src')
| -rw-r--r-- | compiler/rustc_mir_transform/src/check_undefined_transmutes.rs | 77 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs | 136 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/coverage/spans.rs | 38 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/elaborate_drop.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/errors.rs | 27 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/gvn.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/large_enums.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/lib.rs | 4 |
8 files changed, 27 insertions, 281 deletions
diff --git a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs deleted file mode 100644 index daddb5dedbc..00000000000 --- a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs +++ /dev/null @@ -1,77 +0,0 @@ -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; -use rustc_middle::ty::{AssocItem, AssocKind, TyCtxt}; -use rustc_session::lint::builtin::PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS; -use rustc_span::sym; - -use crate::errors; - -/// Check for transmutes that exhibit undefined behavior. -/// For example, transmuting pointers to integers in a const context. -pub(super) struct CheckUndefinedTransmutes; - -impl<'tcx> crate::MirLint<'tcx> for CheckUndefinedTransmutes { - fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - let mut checker = UndefinedTransmutesChecker { body, tcx }; - checker.visit_body(body); - } -} - -struct UndefinedTransmutesChecker<'a, 'tcx> { - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'tcx> UndefinedTransmutesChecker<'a, 'tcx> { - // This functions checks two things: - // 1. `function` takes a raw pointer as input and returns an integer as output. - // 2. `function` is called from a const function or an associated constant. - // - // Why do we consider const functions and associated constants only? - // - // Generally, undefined behavior in const items are handled by the evaluator. - // But, const functions and associated constants are evaluated only when referenced. - // This can result in undefined behavior in a library going unnoticed until - // the function or constant is actually used. - // - // Therefore, we only consider const functions and associated constants here and leave - // other const items to be handled by the evaluator. - fn is_ptr_to_int_in_const(&self, function: &Operand<'tcx>) -> bool { - let def_id = self.body.source.def_id(); - - if self.tcx.is_const_fn(def_id) - || matches!( - self.tcx.opt_associated_item(def_id), - Some(AssocItem { kind: AssocKind::Const { .. }, .. }) - ) - { - let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); - if let [input] = fn_sig.inputs() { - return input.is_raw_ptr() && fn_sig.output().is_integral(); - } - } - false - } -} - -impl<'tcx> Visitor<'tcx> for UndefinedTransmutesChecker<'_, '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, .. } = &terminator.kind - && let Some((func_def_id, _)) = func.const_fn_def() - && self.tcx.is_intrinsic(func_def_id, sym::transmute) - && self.is_ptr_to_int_in_const(func) - && let Some(call_id) = self.body.source.def_id().as_local() - { - let hir_id = self.tcx.local_def_id_to_hir_id(call_id); - let span = self.body.source_info(location).span; - self.tcx.emit_node_span_lint( - PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, - hir_id, - span, - errors::UndefinedTransmute, - ); - } - } -} diff --git a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs deleted file mode 100644 index 1a3715465ad..00000000000 --- a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs +++ /dev/null @@ -1,136 +0,0 @@ -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`. -/// We chose not to lint u8 -> bool transmutes, see #140431 -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, - is_in_const: bool, - ) -> 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})")), - // char (→ u32) → i32 - (Char, Int(IntTy::I32)) => err(format!("u32::from({arg}).cast_signed()")), - // u32 → char - (Uint(UintTy::U32), Char) => Error { - sugg: format!("char::from_u32_unchecked({arg})"), - help: Some("consider `char::from_u32(…).unwrap()`"), - span, - }, - // i32 → char - (Int(IntTy::I32), Char) => Error { - sugg: format!("char::from_u32_unchecked(i32::cast_unsigned({arg}))"), - help: Some("consider `char::from_u32(i32::cast_unsigned(…)).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 → xsize - (Float(ty), Uint(UintTy::Usize)) => { - err(format!("{}::to_bits({arg}) as usize", ty.name_str())) - } - (Float(ty), Int(IntTy::Isize)) => { - err(format!("{}::to_bits({arg}) as isize", ty.name_str())) - } - // fNN (→ uNN) → iNN - (Float(ty), Int(..)) => err(format!("{}::to_bits({arg}).cast_signed()", ty.name_str())), - // fNN → uNN - (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())), - // xsize → fNN - (Uint(UintTy::Usize) | Int(IntTy::Isize), Float(ty)) => { - err(format!("{}::from_bits({arg} as _)", ty.name_str(),)) - } - // iNN (→ uNN) → fNN - (Int(int_ty), Float(ty)) => err(format!( - "{}::from_bits({}::cast_unsigned({arg}))", - ty.name_str(), - int_ty.name_str() - )), - // uNN → fNN - (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())), - // bool → { x8 } in const context since `From::from` is not const yet - // FIXME: is it possible to know when the parentheses arent necessary? - // FIXME(const_traits): Remove this when From::from is constified? - (Bool, Int(..) | Uint(..)) if is_in_const => { - err(format!("({arg}) as {}", fn_sig.output())) - } - // " using `x8::from` - (Bool, Int(..) | Uint(..)) => err(format!("{}::from({arg})", fn_sig.output())), - _ => 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 Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg) - && let def_id = self.body.source.def_id() - && let Some(lint) = self.is_unnecessary_transmute( - func, - snippet, - span, - self.tcx.hir_body_const_context(def_id.expect_local()).is_some(), - ) - && 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/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index ddeae093df5..ec76076020e 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,8 +1,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; use rustc_middle::ty::TyCtxt; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span}; +use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span}; use tracing::instrument; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; @@ -84,18 +83,8 @@ pub(super) fn extract_refined_covspans<'tcx>( // Discard any span that overlaps with a hole. discard_spans_overlapping_holes(&mut covspans, &holes); - // Discard spans that overlap in unwanted ways. + // Perform more refinement steps after holes have been dealt with. let mut covspans = remove_unwanted_overlapping_spans(covspans); - - // For all empty spans, either enlarge them to be non-empty, or discard them. - let source_map = tcx.sess.source_map(); - covspans.retain_mut(|covspan| { - let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false }; - covspan.span = span; - true - }); - - // Merge covspans that can be merged. covspans.dedup_by(|b, a| a.merge_if_eligible(b)); code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| { @@ -241,26 +230,3 @@ fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering { // - Both have the same start and span A extends further right .then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse()) } - -fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> { - if !span.is_empty() { - return Some(span); - } - - // The span is empty, so try to enlarge it to cover an adjacent '{' or '}'. - source_map - .span_to_source(span, |src, start, end| try { - // Adjusting span endpoints by `BytePos(1)` is normally a bug, - // but in this case we have specifically checked that the character - // we're skipping over is one of two specific ASCII characters, so - // adjusting by exactly 1 byte is correct. - if src.as_bytes().get(end).copied() == Some(b'{') { - Some(span.with_hi(span.hi() + BytePos(1))) - } else if start > 0 && src.as_bytes()[start - 1] == b'}' { - Some(span.with_lo(span.lo() - BytePos(1))) - } else { - None - } - }) - .ok()? -} diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 14f7c2a263b..211e2a92f73 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -1278,6 +1278,23 @@ where } ty::Slice(ety) => self.drop_loop_trio_for_slice(*ety), + ty::UnsafeBinder(_) => { + // Unsafe binders may elaborate drops if their inner type isn't copy. + // This is enforced in typeck, so this should never happen. + self.tcx().dcx().span_delayed_bug( + self.source_info.span, + "open drop for unsafe binder shouldn't be encountered", + ); + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Unreachable, + }), + is_cleanup: self.unwind.is_cleanup(), + }) + } + _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty), } } diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 5b03a4987ed..cffa0183fa7 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -158,33 +158,6 @@ 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] -#[note(mir_transform_note2)] -#[help] -pub(crate) struct UndefinedTransmute; - #[derive(Diagnostic)] #[diag(mir_transform_force_inline)] #[note] diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 209e818e9e3..a91d46ec406 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -638,6 +638,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { place: PlaceRef<'tcx>, value: VnIndex, proj: PlaceElem<'tcx>, + from_non_ssa_index: &mut bool, ) -> Option<VnIndex> { let proj = match proj { ProjectionElem::Deref => { @@ -682,6 +683,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { + *from_non_ssa_index |= self.locals[idx].is_none(); return Some(*inner); } let idx = self.locals[idx]?; @@ -774,6 +776,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Invariant: `value` holds the value up-to the `index`th projection excluded. let mut value = self.locals[place.local]?; + let mut from_non_ssa_index = false; for (index, proj) in place.projection.iter().enumerate() { if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer) @@ -791,7 +794,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let base = PlaceRef { local: place.local, projection: &place.projection[..index] }; - value = self.project(base, value, proj)?; + value = self.project(base, value, proj, &mut from_non_ssa_index)?; } if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) @@ -804,6 +807,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } if let Some(new_local) = self.try_as_local(value, location) { place_ref = PlaceRef { local: new_local, projection: &[] }; + } else if from_non_ssa_index { + // If access to non-SSA locals is unavoidable, bail out. + return None; } if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() { diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 47cb478fe33..1a91d6bd7da 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -241,6 +241,7 @@ impl EnumSizeOpt { data, tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi, Mutability::Not, + (), ); let alloc = tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc)); Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc))) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 10dbb3437dc..d26e4468715 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -123,8 +123,6 @@ declare_passes! { mod check_const_item_mutation : CheckConstItemMutation; 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; @@ -390,8 +388,6 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> { &Lint(check_packed_ref::CheckPackedRef), &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), |
