about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-10-31 23:08:56 +0000
committerbors <bors@rust-lang.org>2023-10-31 23:08:56 +0000
commit09ac6e4b6dbec74dc4718191a6650a1b7e04a7a6 (patch)
treeab82fb13591c51d4259b8a026a3f58984b2a8f11
parent9d83ac217957eece2189eccf4a7232caec7232ee (diff)
parentd06200b988d9e0242ffc55950e95f9afc7a0c6ff (diff)
downloadrust-09ac6e4b6dbec74dc4718191a6650a1b7e04a7a6.tar.gz
rust-09ac6e4b6dbec74dc4718191a6650a1b7e04a7a6.zip
Auto merge of #117459 - matthiaskrgr:rollup-t3osb3c, r=matthiaskrgr
Rollup of 5 pull requests

Successful merges:

 - #113241 (rustdoc: Document lack of object safety on affected traits)
 - #117388 (Turn const_caller_location from a query to a hook)
 - #117417 (Add a stable MIR visitor)
 - #117439 (prepopulate opaque ty storage before using it)
 - #117451 (Add support for pre-unix-epoch file dates on Apple platforms (#108277))

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs14
-rw-r--r--compiler/rustc_const_eval/src/lib.rs2
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs13
-rw-r--r--compiler/rustc_middle/src/hooks/mod.rs3
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs4
-rw-r--r--compiler/rustc_middle/src/query/mod.rs4
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs6
-rw-r--r--compiler/stable_mir/src/lib.rs7
-rw-r--r--compiler/stable_mir/src/mir.rs2
-rw-r--r--compiler/stable_mir/src/mir/body.rs74
-rw-r--r--compiler/stable_mir/src/mir/mono.rs4
-rw-r--r--compiler/stable_mir/src/mir/visit.rs414
-rw-r--r--compiler/stable_mir/src/ty.rs92
-rw-r--r--library/std/src/fs/tests.rs42
-rw-r--r--library/std/src/sys/unix/time.rs24
-rw-r--r--src/librustdoc/clean/types.rs3
-rw-r--r--src/librustdoc/html/markdown.rs1
-rw-r--r--src/librustdoc/html/render/print_item.rs15
-rw-r--r--src/librustdoc/html/render/sidebar.rs8
-rw-r--r--tests/rustdoc/sidebar-items.rs7
-rw-r--r--tests/rustdoc/trait-object-safe.rs27
-rw-r--r--tests/ui-fulldeps/stable-mir/smir_visitor.rs148
22 files changed, 811 insertions, 103 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 8477686d4f5..9f30d9d8ba1 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -188,11 +188,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
         &mut borrowck_context,
     );
 
-    // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
-    // predefined opaques in the typeck root.
-    if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
-        checker.register_predefined_opaques_in_new_solver();
-    }
+    checker.check_user_type_annotations();
 
     let mut verifier = TypeVerifier::new(&mut checker, promoted);
     verifier.visit_body(&body);
@@ -1021,7 +1017,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             borrowck_context,
             reported_errors: Default::default(),
         };
-        checker.check_user_type_annotations();
+
+        // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
+        // predefined opaques in the typeck root.
+        if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
+            checker.register_predefined_opaques_in_new_solver();
+        }
+
         checker
     }
 
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 7fc87ed8881..7d36e2eaefe 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -49,7 +49,7 @@ pub fn provide(providers: &mut Providers) {
     const_eval::provide(providers);
     providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
     providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider;
-    providers.const_caller_location = util::caller_location::const_caller_location_provider;
+    providers.hooks.const_caller_location = util::caller_location::const_caller_location_provider;
     providers.eval_to_valtree = |tcx, param_env_and_value| {
         let (param_env, raw) = param_env_and_value.into_parts();
         const_eval::eval_to_valtree(tcx, param_env, raw)
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index d67863d75d6..4a3cfd50b44 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -1,8 +1,9 @@
 use rustc_hir::LangItem;
 use rustc_middle::mir;
+use rustc_middle::query::TyCtxtAt;
+use rustc_middle::ty;
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_middle::ty::{self, TyCtxt};
-use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
+use rustc_span::symbol::Symbol;
 use rustc_type_ir::Mutability;
 
 use crate::const_eval::{mk_eval_cx, CanAccessStatics, CompileTimeEvalContext};
@@ -49,11 +50,13 @@ fn alloc_caller_location<'mir, 'tcx>(
 }
 
 pub(crate) fn const_caller_location_provider(
-    tcx: TyCtxt<'_>,
-    (file, line, col): (Symbol, u32, u32),
+    tcx: TyCtxtAt<'_>,
+    file: Symbol,
+    line: u32,
+    col: u32,
 ) -> mir::ConstValue<'_> {
     trace!("const_caller_location: {}:{}:{}", file, line, col);
-    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
+    let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
 
     let loc_place = alloc_caller_location(&mut ecx, file, line, col);
     if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs
index 572751d9511..34838ca4302 100644
--- a/compiler/rustc_middle/src/hooks/mod.rs
+++ b/compiler/rustc_middle/src/hooks/mod.rs
@@ -67,4 +67,7 @@ declare_hooks! {
     /// Tries to destructure an `mir::Const` ADT or array into its variant index
     /// and its field values. This should only be used for pretty printing.
     hook try_destructure_mir_constant_for_diagnostics(val: mir::ConstValue<'tcx>, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>;
+
+    /// Getting a &core::panic::Location referring to a span.
+    hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>;
 }
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 92218f6948f..a9d09709e84 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -590,12 +590,12 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> {
         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
         let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
-        self.const_caller_location((
+        self.const_caller_location(
             rustc_span::symbol::Symbol::intern(
                 &caller.file.name.for_codegen(&self.sess).to_string_lossy(),
             ),
             caller.line as u32,
             caller.col_display as u32 + 1,
-        ))
+        )
     }
 }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 062b03e71fd..f9ec368361c 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1101,10 +1101,6 @@ rustc_queries! {
         desc { "destructuring type level constant"}
     }
 
-    query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> mir::ConstValue<'tcx> {
-        desc { "getting a &core::panic::Location referring to a span" }
-    }
-
     // FIXME get rid of this with valtrees
     query lit_to_const(
         key: LitToConstInput<'tcx>
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 5ab5a048ffa..b619ce6e35f 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -216,6 +216,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         tables.create_def_id(def_id)
     }
 
+    fn instance_mangled_name(&self, def: InstanceDef) -> String {
+        let tables = self.0.borrow_mut();
+        let instance = tables.instances[def];
+        tables.tcx.symbol_name(instance).name.to_string()
+    }
+
     fn mono_instance(&self, item: stable_mir::CrateItem) -> stable_mir::mir::mono::Instance {
         let mut tables = self.0.borrow_mut();
         let def_id = tables[item.0];
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
index 38915afaa0c..f316671b278 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -103,8 +103,6 @@ pub type DefKind = Opaque;
 pub type Filename = Opaque;
 
 /// Holds information about an item in the crate.
-/// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to
-/// use this item.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct CrateItem(pub DefId);
 
@@ -224,6 +222,9 @@ pub trait Context {
     /// Get the instance.
     fn instance_def_id(&self, instance: InstanceDef) -> DefId;
 
+    /// Get the instance mangled name.
+    fn instance_mangled_name(&self, instance: InstanceDef) -> String;
+
     /// Convert a non-generic crate item into an instance.
     /// This function will panic if the item is generic.
     fn mono_instance(&self, item: CrateItem) -> Instance;
@@ -259,7 +260,7 @@ pub fn with<R>(f: impl FnOnce(&dyn Context) -> R) -> R {
 }
 
 /// A type that provides internal information but that can still be used for debug purpose.
-#[derive(Clone)]
+#[derive(Clone, Eq, PartialEq)]
 pub struct Opaque(String);
 
 impl std::fmt::Display for Opaque {
diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs
index 3138bb1ec83..2e1714b49c1 100644
--- a/compiler/stable_mir/src/mir.rs
+++ b/compiler/stable_mir/src/mir.rs
@@ -1,4 +1,6 @@
 mod body;
 pub mod mono;
+pub mod visit;
 
 pub use body::*;
+pub use visit::MirVisitor;
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 9f69e61d6fe..804f5e3d9d0 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -1,6 +1,6 @@
-use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region};
+use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, Ty};
 use crate::Opaque;
-use crate::{ty::Ty, Span};
+use crate::Span;
 
 /// The SMIR representation of a single function.
 #[derive(Clone, Debug)]
@@ -12,10 +12,10 @@ pub struct Body {
     // The first local is the return value pointer, followed by `arg_count`
     // locals for the function arguments, followed by any user-declared
     // variables and temporaries.
-    locals: LocalDecls,
+    pub(super) locals: LocalDecls,
 
     // The number of arguments this function takes.
-    arg_count: usize,
+    pub(super) arg_count: usize,
 }
 
 impl Body {
@@ -35,7 +35,7 @@ impl Body {
 
     /// Return local that holds this function's return value.
     pub fn ret_local(&self) -> &LocalDecl {
-        &self.locals[0]
+        &self.locals[RETURN_LOCAL]
     }
 
     /// Locals in `self` that correspond to this function's arguments.
@@ -60,7 +60,7 @@ impl Body {
 
 type LocalDecls = Vec<LocalDecl>;
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct LocalDecl {
     pub ty: Ty,
     pub span: Span,
@@ -72,13 +72,13 @@ pub struct BasicBlock {
     pub terminator: Terminator,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Terminator {
     pub kind: TerminatorKind,
     pub span: Span,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum TerminatorKind {
     Goto {
         target: usize,
@@ -122,7 +122,7 @@ pub enum TerminatorKind {
     },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct InlineAsmOperand {
     pub in_value: Option<Operand>,
     pub out_place: Option<Place>,
@@ -131,7 +131,7 @@ pub struct InlineAsmOperand {
     pub raw_rpr: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum UnwindAction {
     Continue,
     Unreachable,
@@ -139,7 +139,7 @@ pub enum UnwindAction {
     Cleanup(usize),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum AssertMessage {
     BoundsCheck { len: Operand, index: Operand },
     Overflow(BinOp, Operand, Operand),
@@ -151,7 +151,7 @@ pub enum AssertMessage {
     MisalignedPointerDereference { required: Operand, found: Operand },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum BinOp {
     Add,
     AddUnchecked,
@@ -177,20 +177,20 @@ pub enum BinOp {
     Offset,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum UnOp {
     Not,
     Neg,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CoroutineKind {
     Async(CoroutineSource),
     Coroutine,
     Gen(CoroutineSource),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CoroutineSource {
     Block,
     Closure,
@@ -204,7 +204,7 @@ pub(crate) type LocalDefId = Opaque;
 pub(crate) type Coverage = Opaque;
 
 /// The FakeReadCause describes the type of pattern why a FakeRead statement exists.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum FakeReadCause {
     ForMatchGuard,
     ForMatchedPlace(LocalDefId),
@@ -214,7 +214,7 @@ pub enum FakeReadCause {
 }
 
 /// Describes what kind of retag is to be performed
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub enum RetagKind {
     FnEntry,
     TwoPhase,
@@ -222,7 +222,7 @@ pub enum RetagKind {
     Default,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub enum Variance {
     Covariant,
     Invariant,
@@ -230,26 +230,26 @@ pub enum Variance {
     Bivariant,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct CopyNonOverlapping {
     pub src: Operand,
     pub dst: Operand,
     pub count: Operand,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum NonDivergingIntrinsic {
     Assume(Operand),
     CopyNonOverlapping(CopyNonOverlapping),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Statement {
     pub kind: StatementKind,
     pub span: Span,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum StatementKind {
     Assign(Place, Rvalue),
     FakeRead(FakeReadCause, Place),
@@ -266,7 +266,7 @@ pub enum StatementKind {
     Nop,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Rvalue {
     /// Creates a pointer with the indicated mutability to the place.
     ///
@@ -378,7 +378,7 @@ pub enum Rvalue {
     Use(Operand),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum AggregateKind {
     Array(Ty),
     Tuple,
@@ -387,21 +387,21 @@ pub enum AggregateKind {
     Coroutine(CoroutineDef, GenericArgs, Movability),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Operand {
     Copy(Place),
     Move(Place),
     Constant(Constant),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Place {
     pub local: Local,
     /// projection out of a place (access a field, deref a pointer, etc)
     pub projection: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct UserTypeProjection {
     pub base: UserTypeAnnotationIndex,
     pub projection: String,
@@ -409,6 +409,8 @@ pub struct UserTypeProjection {
 
 pub type Local = usize;
 
+pub const RETURN_LOCAL: Local = 0;
+
 type FieldIdx = usize;
 
 /// The source-order index of a variant in a type.
@@ -416,20 +418,20 @@ pub type VariantIdx = usize;
 
 type UserTypeAnnotationIndex = usize;
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Constant {
     pub span: Span,
     pub user_ty: Option<UserTypeAnnotationIndex>,
     pub literal: Const,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SwitchTarget {
     pub value: u128,
     pub target: usize,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
     Shared,
@@ -446,26 +448,26 @@ pub enum BorrowKind {
     },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum MutBorrowKind {
     Default,
     TwoPhaseBorrow,
     ClosureCapture,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Mutability {
     Not,
     Mut,
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum Safety {
     Unsafe,
     Normal,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum PointerCoercion {
     /// Go from a fn-item type to a fn-pointer type.
     ReifyFnPointer,
@@ -492,7 +494,7 @@ pub enum PointerCoercion {
     Unsize,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CastKind {
     PointerExposeAddress,
     PointerFromExposedAddress,
@@ -507,7 +509,7 @@ pub enum CastKind {
     Transmute,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum NullOp {
     /// Returns the size of a value of that type.
     SizeOf,
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs
index 997576fc7cb..8f533349848 100644
--- a/compiler/stable_mir/src/mir/mono.rs
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -42,6 +42,10 @@ impl Instance {
         with(|context| context.instance_ty(self.def))
     }
 
+    pub fn mangled_name(&self) -> String {
+        with(|context| context.instance_mangled_name(self.def))
+    }
+
     /// Resolve an instance starting from a function definition and generic arguments.
     pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> {
         with(|context| {
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
new file mode 100644
index 00000000000..806dced71ff
--- /dev/null
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -0,0 +1,414 @@
+//! # The Stable MIR Visitor
+//!
+//! ## Overview
+//!
+//! We currently only support an immutable visitor.
+//! The structure of this visitor is similar to the ones internal to `rustc`,
+//! and it follows the following conventions:
+//!
+//! For every mir item, the trait has a `visit_<item>` and a `super_<item>` method.
+//! - `visit_<item>`, by default, calls `super_<item>`
+//! - `super_<item>`, by default, destructures the `<item>` and calls `visit_<sub_item>` for
+//!   all sub-items that compose the original item.
+//!
+//! In order to implement a visitor, override the `visit_*` methods for the types you are
+//! interested in analyzing, and invoke (within that method call)
+//! `self.super_*` to continue to the traverse.
+//! Avoid calling `super` methods in other circumstances.
+//!
+//! For the most part, we do not destructure things external to the
+//! MIR, e.g., types, spans, etc, but simply visit them and stop.
+//! This avoids duplication with other visitors like `TypeFoldable`.
+//!
+//! ## Updating
+//!
+//! The code is written in a very deliberate style intended to minimize
+//! the chance of things being overlooked.
+//!
+//! Use pattern matching to reference fields and ensure that all
+//! matches are exhaustive.
+//!
+//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS.
+//! That means you never write `..` to skip over fields, nor do you write `_`
+//! to skip over variants in a `match`.
+//!
+//! The only place that `_` is acceptable is to match a field (or
+//! variant argument) that does not require visiting.
+
+use crate::mir::*;
+use crate::ty::{Const, GenericArgs, Region, Ty};
+use crate::{Opaque, Span};
+
+pub trait MirVisitor {
+    fn visit_body(&mut self, body: &Body) {
+        self.super_body(body)
+    }
+
+    fn visit_basic_block(&mut self, bb: &BasicBlock) {
+        self.super_basic_block(bb)
+    }
+
+    fn visit_ret_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_ret_decl(local, decl)
+    }
+
+    fn visit_arg_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_arg_decl(local, decl)
+    }
+
+    fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_local_decl(local, decl)
+    }
+
+    fn visit_statement(&mut self, stmt: &Statement, location: Location) {
+        self.super_statement(stmt, location)
+    }
+
+    fn visit_terminator(&mut self, term: &Terminator, location: Location) {
+        self.super_terminator(term, location)
+    }
+
+    fn visit_span(&mut self, span: &Span) {
+        self.super_span(span)
+    }
+
+    fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
+        self.super_place(place, ptx, location)
+    }
+
+    fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) {
+        let _ = (local, ptx, location);
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
+        self.super_rvalue(rvalue, location)
+    }
+
+    fn visit_operand(&mut self, operand: &Operand, location: Location) {
+        self.super_operand(operand, location)
+    }
+
+    fn visit_user_type_projection(&mut self, projection: &UserTypeProjection) {
+        self.super_user_type_projection(projection)
+    }
+
+    fn visit_ty(&mut self, ty: &Ty, location: Location) {
+        let _ = location;
+        self.super_ty(ty)
+    }
+
+    fn visit_constant(&mut self, constant: &Constant, location: Location) {
+        self.super_constant(constant, location)
+    }
+
+    fn visit_const(&mut self, constant: &Const, location: Location) {
+        self.super_const(constant, location)
+    }
+
+    fn visit_region(&mut self, region: &Region, location: Location) {
+        let _ = location;
+        self.super_region(region)
+    }
+
+    fn visit_args(&mut self, args: &GenericArgs, location: Location) {
+        let _ = location;
+        self.super_args(args)
+    }
+
+    fn visit_assert_msg(&mut self, msg: &AssertMessage, location: Location) {
+        self.super_assert_msg(msg, location)
+    }
+
+    fn super_body(&mut self, body: &Body) {
+        let Body { blocks, locals: _, arg_count } = body;
+
+        for bb in blocks {
+            self.visit_basic_block(bb);
+        }
+
+        self.visit_ret_decl(RETURN_LOCAL, body.ret_local());
+
+        for (idx, arg) in body.arg_locals().iter().enumerate() {
+            self.visit_arg_decl(idx + 1, arg)
+        }
+
+        let local_start = arg_count + 1;
+        for (idx, arg) in body.arg_locals().iter().enumerate() {
+            self.visit_local_decl(idx + local_start, arg)
+        }
+    }
+
+    fn super_basic_block(&mut self, bb: &BasicBlock) {
+        let BasicBlock { statements, terminator } = bb;
+        for stmt in statements {
+            self.visit_statement(stmt, Location(stmt.span));
+        }
+        self.visit_terminator(terminator, Location(terminator.span));
+    }
+
+    fn super_local_decl(&mut self, local: Local, decl: &LocalDecl) {
+        let _ = local;
+        let LocalDecl { ty, span } = decl;
+        self.visit_ty(ty, Location(*span));
+    }
+
+    fn super_ret_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_local_decl(local, decl)
+    }
+
+    fn super_arg_decl(&mut self, local: Local, decl: &LocalDecl) {
+        self.super_local_decl(local, decl)
+    }
+
+    fn super_statement(&mut self, stmt: &Statement, location: Location) {
+        let Statement { kind, span } = stmt;
+        self.visit_span(span);
+        match kind {
+            StatementKind::Assign(place, rvalue) => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+                self.visit_rvalue(rvalue, location);
+            }
+            StatementKind::FakeRead(_, place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location);
+            }
+            StatementKind::SetDiscriminant { place, .. } => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            StatementKind::Deinit(place) => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            StatementKind::StorageLive(local) => {
+                self.visit_local(local, PlaceContext::NON_USE, location);
+            }
+            StatementKind::StorageDead(local) => {
+                self.visit_local(local, PlaceContext::NON_USE, location);
+            }
+            StatementKind::Retag(_, place) => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            StatementKind::PlaceMention(place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location);
+            }
+            StatementKind::AscribeUserType { place, projections, variance: _ } => {
+                self.visit_place(place, PlaceContext::NON_USE, location);
+                self.visit_user_type_projection(projections);
+            }
+            StatementKind::Coverage(coverage) => visit_opaque(coverage),
+            StatementKind::Intrinsic(intrisic) => match intrisic {
+                NonDivergingIntrinsic::Assume(operand) => {
+                    self.visit_operand(operand, location);
+                }
+                NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
+                    src,
+                    dst,
+                    count,
+                }) => {
+                    self.visit_operand(src, location);
+                    self.visit_operand(dst, location);
+                    self.visit_operand(count, location);
+                }
+            },
+            StatementKind::ConstEvalCounter => {}
+            StatementKind::Nop => {}
+        }
+    }
+
+    fn super_terminator(&mut self, term: &Terminator, location: Location) {
+        let Terminator { kind, span } = term;
+        self.visit_span(&span);
+        match kind {
+            TerminatorKind::Goto { .. }
+            | TerminatorKind::Resume
+            | TerminatorKind::Abort
+            | TerminatorKind::Unreachable
+            | TerminatorKind::CoroutineDrop => {}
+            TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
+                self.visit_operand(cond, location);
+                self.visit_assert_msg(msg, location);
+            }
+            TerminatorKind::Drop { place, target: _, unwind: _ } => {
+                self.visit_place(place, PlaceContext::MUTATING, location);
+            }
+            TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => {
+                self.visit_operand(func, location);
+                for arg in args {
+                    self.visit_operand(arg, location);
+                }
+                self.visit_place(destination, PlaceContext::MUTATING, location);
+            }
+            TerminatorKind::InlineAsm { operands, .. } => {
+                for op in operands {
+                    let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op;
+                    if let Some(input) = in_value {
+                        self.visit_operand(input, location);
+                    }
+                    if let Some(output) = out_place {
+                        self.visit_place(output, PlaceContext::MUTATING, location);
+                    }
+                }
+            }
+            TerminatorKind::Return => {
+                let local = RETURN_LOCAL;
+                self.visit_local(&local, PlaceContext::NON_MUTATING, location);
+            }
+            TerminatorKind::SwitchInt { discr, targets: _, otherwise: _ } => {
+                self.visit_operand(discr, location);
+            }
+        }
+    }
+
+    fn super_span(&mut self, span: &Span) {
+        let _ = span;
+    }
+
+    fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
+        let _ = location;
+        let _ = ptx;
+        visit_opaque(&Opaque(place.projection.clone()));
+    }
+
+    fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
+        match rvalue {
+            Rvalue::AddressOf(mutability, place) => {
+                let pcx = PlaceContext { is_mut: *mutability == Mutability::Mut };
+                self.visit_place(place, pcx, location);
+            }
+            Rvalue::Aggregate(_, operands) => {
+                for op in operands {
+                    self.visit_operand(op, location);
+                }
+            }
+            Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
+                self.visit_operand(lhs, location);
+                self.visit_operand(rhs, location);
+            }
+            Rvalue::Cast(_, op, ty) => {
+                self.visit_operand(op, location);
+                self.visit_ty(ty, location);
+            }
+            Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location);
+            }
+            Rvalue::Ref(region, kind, place) => {
+                self.visit_region(region, location);
+                let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) };
+                self.visit_place(place, pcx, location);
+            }
+            Rvalue::Repeat(op, constant) => {
+                self.visit_operand(op, location);
+                self.visit_const(constant, location);
+            }
+            Rvalue::ShallowInitBox(op, ty) => {
+                self.visit_ty(ty, location);
+                self.visit_operand(op, location)
+            }
+            Rvalue::ThreadLocalRef(_) => {}
+            Rvalue::NullaryOp(_, ty) => {
+                self.visit_ty(ty, location);
+            }
+            Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => {
+                self.visit_operand(op, location);
+            }
+        }
+    }
+
+    fn super_operand(&mut self, operand: &Operand, location: Location) {
+        match operand {
+            Operand::Copy(place) | Operand::Move(place) => {
+                self.visit_place(place, PlaceContext::NON_MUTATING, location)
+            }
+            Operand::Constant(constant) => {
+                self.visit_constant(constant, location);
+            }
+        }
+    }
+
+    fn super_user_type_projection(&mut self, projection: &UserTypeProjection) {
+        // This is a no-op on mir::Visitor.
+        let _ = projection;
+    }
+
+    fn super_ty(&mut self, ty: &Ty) {
+        let _ = ty;
+    }
+
+    fn super_constant(&mut self, constant: &Constant, location: Location) {
+        let Constant { span, user_ty: _, literal } = constant;
+        self.visit_span(span);
+        self.visit_const(literal, location);
+    }
+
+    fn super_const(&mut self, constant: &Const, location: Location) {
+        let Const { kind: _, ty, id: _ } = constant;
+        self.visit_ty(ty, location);
+    }
+
+    fn super_region(&mut self, region: &Region) {
+        let _ = region;
+    }
+
+    fn super_args(&mut self, args: &GenericArgs) {
+        let _ = args;
+    }
+
+    fn super_assert_msg(&mut self, msg: &AssertMessage, location: Location) {
+        match msg {
+            AssertMessage::BoundsCheck { len, index } => {
+                self.visit_operand(len, location);
+                self.visit_operand(index, location);
+            }
+            AssertMessage::Overflow(_, left, right) => {
+                self.visit_operand(left, location);
+                self.visit_operand(right, location);
+            }
+            AssertMessage::OverflowNeg(op)
+            | AssertMessage::DivisionByZero(op)
+            | AssertMessage::RemainderByZero(op) => {
+                self.visit_operand(op, location);
+            }
+            AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { //nothing to visit
+            }
+            AssertMessage::MisalignedPointerDereference { required, found } => {
+                self.visit_operand(required, location);
+                self.visit_operand(found, location);
+            }
+        }
+    }
+}
+
+/// This function is a no-op that gets used to ensure this visitor is kept up-to-date.
+///
+/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail
+/// when trying to invoke `visit_opaque`.
+///
+/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()`
+/// by a `visit_<CONSTRUCT>` for your construct.
+fn visit_opaque(_: &Opaque) {}
+
+/// The location of a statement / terminator in the code and the CFG.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Location(Span);
+
+impl Location {
+    pub fn span(&self) -> Span {
+        self.0
+    }
+}
+
+/// Information about a place's usage.
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct PlaceContext {
+    /// Whether the access is mutable or not. Keep this private so we can increment the type in a
+    /// backward compatible manner.
+    is_mut: bool,
+}
+
+impl PlaceContext {
+    const MUTATING: Self = PlaceContext { is_mut: true };
+    const NON_MUTATING: Self = PlaceContext { is_mut: false };
+    const NON_USE: Self = PlaceContext { is_mut: false };
+
+    pub fn is_mutating(&self) -> bool {
+        self.is_mut
+    }
+}
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index e7440cc439b..5dfaa0fd891 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -22,12 +22,12 @@ impl Ty {
 }
 
 /// Represents a constant in MIR or from the Type system.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Const {
     /// The constant kind.
-    kind: ConstantKind,
+    pub(crate) kind: ConstantKind,
     /// The constant type.
-    ty: Ty,
+    pub(crate) ty: Ty,
     /// Used for internal tracking of the internal constant.
     pub id: ConstId,
 }
@@ -54,12 +54,12 @@ pub struct ConstId(pub usize);
 
 type Ident = Opaque;
 
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Region {
     pub kind: RegionKind,
 }
 
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum RegionKind {
     ReEarlyBound(EarlyBoundRegion),
     ReLateBound(DebruijnIndex, BoundRegion),
@@ -70,7 +70,7 @@ pub enum RegionKind {
 
 pub(crate) type DebruijnIndex = u32;
 
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct EarlyBoundRegion {
     pub def_id: RegionDef,
     pub index: u32,
@@ -79,7 +79,7 @@ pub struct EarlyBoundRegion {
 
 pub(crate) type BoundVar = u32;
 
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct BoundRegion {
     pub var: BoundVar,
     pub kind: BoundRegionKind,
@@ -87,7 +87,7 @@ pub struct BoundRegion {
 
 pub(crate) type UniverseIndex = u32;
 
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Placeholder<T> {
     pub universe: UniverseIndex,
     pub bound: T,
@@ -127,7 +127,7 @@ pub struct LineInfo {
     pub end_col: usize,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum TyKind {
     RigidTy(RigidTy),
     Alias(AliasKind, AliasTy),
@@ -135,7 +135,7 @@ pub enum TyKind {
     Bound(usize, BoundTy),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum RigidTy {
     Bool,
     Char,
@@ -236,7 +236,7 @@ pub struct ImplDef(pub DefId);
 pub struct RegionDef(pub DefId);
 
 /// A list of generic arguments.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct GenericArgs(pub Vec<GenericArgKind>);
 
 impl std::ops::Index<ParamTy> for GenericArgs {
@@ -255,7 +255,7 @@ impl std::ops::Index<ParamConst> for GenericArgs {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum GenericArgKind {
     Lifetime(Region),
     Type(Ty),
@@ -284,13 +284,13 @@ impl GenericArgKind {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum TermKind {
     Type(Ty),
     Const(Const),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum AliasKind {
     Projection,
     Inherent,
@@ -298,7 +298,7 @@ pub enum AliasKind {
     Weak,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct AliasTy {
     pub def_id: AliasDef,
     pub args: GenericArgs,
@@ -306,7 +306,7 @@ pub struct AliasTy {
 
 pub type PolyFnSig = Binder<FnSig>;
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct FnSig {
     pub inputs_and_output: Vec<Ty>,
     pub c_variadic: bool,
@@ -345,18 +345,18 @@ pub enum Abi {
     RiscvInterruptS,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Binder<T> {
     pub value: T,
     pub bound_vars: Vec<BoundVariableKind>,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct EarlyBinder<T> {
     pub value: T,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum BoundVariableKind {
     Ty(BoundTyKind),
     Region(BoundRegionKind),
@@ -369,46 +369,46 @@ pub enum BoundTyKind {
     Param(ParamDef, String),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum BoundRegionKind {
     BrAnon,
     BrNamed(BrNamedDef, String),
     BrEnv,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum DynKind {
     Dyn,
     DynStar,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum ExistentialPredicate {
     Trait(ExistentialTraitRef),
     Projection(ExistentialProjection),
     AutoTrait(TraitDef),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ExistentialTraitRef {
     pub def_id: TraitDef,
     pub generic_args: GenericArgs,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ExistentialProjection {
     pub def_id: TraitDef,
     pub generic_args: GenericArgs,
     pub term: TermKind,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ParamTy {
     pub index: u32,
     pub name: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct BoundTy {
     pub var: usize,
     pub kind: BoundTyKind,
@@ -424,14 +424,14 @@ pub type Promoted = u32;
 pub type InitMaskMaterialized = Vec<u64>;
 
 /// Stores the provenance information of pointers stored in memory.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ProvenanceMap {
     /// Provenance in this map applies from the given offset for an entire pointer-size worth of
     /// bytes. Two entries in this map are always at least a pointer size apart.
     pub ptrs: Vec<(Size, Prov)>,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Allocation {
     pub bytes: Bytes,
     pub provenance: ProvenanceMap,
@@ -439,7 +439,7 @@ pub struct Allocation {
     pub mutability: Mutability,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum ConstantKind {
     Allocated(Allocation),
     Unevaluated(UnevaluatedConst),
@@ -449,13 +449,13 @@ pub enum ConstantKind {
     ZeroSized,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ParamConst {
     pub index: u32,
     pub name: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct UnevaluatedConst {
     pub def: ConstDef,
     pub args: GenericArgs,
@@ -469,7 +469,7 @@ pub enum TraitSpecializationKind {
     AlwaysApplicable,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct TraitDecl {
     pub def_id: TraitDef,
     pub unsafety: Safety,
@@ -500,13 +500,13 @@ impl TraitDecl {
 
 pub type ImplTrait = EarlyBinder<TraitRef>;
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct TraitRef {
     pub def_id: TraitDef,
     pub args: GenericArgs,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Generics {
     pub parent: Option<GenericDef>,
     pub parent_count: usize,
@@ -517,14 +517,14 @@ pub struct Generics {
     pub host_effect_index: Option<usize>,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum GenericParamDefKind {
     Lifetime,
     Type { has_default: bool, synthetic: bool },
     Const { has_default: bool },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct GenericParamDef {
     pub name: super::Symbol,
     pub def_id: GenericDef,
@@ -538,7 +538,7 @@ pub struct GenericPredicates {
     pub predicates: Vec<(PredicateKind, Span)>,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum PredicateKind {
     Clause(ClauseKind),
     ObjectSafe(TraitDef),
@@ -550,7 +550,7 @@ pub enum PredicateKind {
     AliasRelate(TermKind, TermKind, AliasRelationDirection),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum ClauseKind {
     Trait(TraitPredicate),
     RegionOutlives(RegionOutlivesPredicate),
@@ -561,50 +561,50 @@ pub enum ClauseKind {
     ConstEvaluatable(Const),
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum ClosureKind {
     Fn,
     FnMut,
     FnOnce,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SubtypePredicate {
     pub a: Ty,
     pub b: Ty,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct CoercePredicate {
     pub a: Ty,
     pub b: Ty,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum AliasRelationDirection {
     Equate,
     Subtype,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct TraitPredicate {
     pub trait_ref: TraitRef,
     pub polarity: ImplPolarity,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct OutlivesPredicate<A, B>(pub A, pub B);
 
 pub type RegionOutlivesPredicate = OutlivesPredicate<Region, Region>;
 pub type TypeOutlivesPredicate = OutlivesPredicate<Ty, Region>;
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ProjectionPredicate {
     pub projection_ty: AliasTy,
     pub term: TermKind,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum ImplPolarity {
     Positive,
     Negative,
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 736b495343e..547a7b7052f 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1709,6 +1709,48 @@ fn test_file_times() {
 }
 
 #[test]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
+fn test_file_times_pre_epoch_with_nanos() {
+    #[cfg(target_os = "ios")]
+    use crate::os::ios::fs::FileTimesExt;
+    #[cfg(target_os = "macos")]
+    use crate::os::macos::fs::FileTimesExt;
+    #[cfg(target_os = "tvos")]
+    use crate::os::tvos::fs::FileTimesExt;
+    #[cfg(target_os = "watchos")]
+    use crate::os::watchos::fs::FileTimesExt;
+
+    let tmp = tmpdir();
+    let file = File::create(tmp.join("foo")).unwrap();
+
+    for (accessed, modified, created) in [
+        // The first round is to set filetimes to something we know works, but this time
+        // it's validated with nanoseconds as well which probe the numeric boundary.
+        (
+            SystemTime::UNIX_EPOCH + Duration::new(12345, 1),
+            SystemTime::UNIX_EPOCH + Duration::new(54321, 100_000_000),
+            SystemTime::UNIX_EPOCH + Duration::new(32123, 999_999_999),
+        ),
+        // The second rounds uses pre-epoch dates along with nanoseconds that probe
+        // the numeric boundary.
+        (
+            SystemTime::UNIX_EPOCH - Duration::new(1, 1),
+            SystemTime::UNIX_EPOCH - Duration::new(60, 100_000_000),
+            SystemTime::UNIX_EPOCH - Duration::new(3600, 999_999_999),
+        ),
+    ] {
+        let mut times = FileTimes::new();
+        times = times.set_accessed(accessed).set_modified(modified).set_created(created);
+        file.set_times(times).unwrap();
+
+        let metadata = file.metadata().unwrap();
+        assert_eq!(metadata.accessed().unwrap(), accessed);
+        assert_eq!(metadata.modified().unwrap(), modified);
+        assert_eq!(metadata.created().unwrap(), created);
+    }
+}
+
+#[test]
 #[cfg(windows)]
 fn windows_unix_socket_exists() {
     use crate::sys::{c, net};
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index e4540b99413..f2e86a4fb2b 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -76,6 +76,30 @@ impl Timespec {
     }
 
     const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
+        // On Apple OS, dates before epoch are represented differently than on other
+        // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1`
+        // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and
+        // `nanoseconds=-900_000_000` on Apple OS.
+        //
+        // To compensate, we first detect this special case by checking if both
+        // seconds and nanoseconds are in range, and then correct the value for seconds
+        // and nanoseconds to match the common unix representation.
+        //
+        // Please note that Apple OS nonetheless accepts the standard unix format when
+        // setting file times, which makes this compensation round-trippable and generally
+        // transparent.
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "tvos",
+            target_os = "watchos"
+        ))]
+        let (tv_sec, tv_nsec) =
+            if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) {
+                (tv_sec - 1, tv_nsec + 1_000_000_000)
+            } else {
+                (tv_sec, tv_nsec)
+            };
         assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
         // SAFETY: The assert above checks tv_nsec is within the valid range
         Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index ba55871c75d..a718cb37d89 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1455,6 +1455,9 @@ impl Trait {
     pub(crate) fn unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety {
         tcx.trait_def(self.def_id).unsafety
     }
+    pub(crate) fn is_object_safe(&self, tcx: TyCtxt<'_>) -> bool {
+        tcx.check_is_object_safe(self.def_id)
+    }
 }
 
 #[derive(Clone, Debug)]
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index d24e6e5faf5..aa728c26afc 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -2024,6 +2024,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
     map.insert("required-associated-consts".into(), 1);
     map.insert("required-methods".into(), 1);
     map.insert("provided-methods".into(), 1);
+    map.insert("object-safety".into(), 1);
     map.insert("implementors".into(), 1);
     map.insert("synthetic-implementors".into(), 1);
     map.insert("implementations-list".into(), 1);
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 1c5a1dc99ad..65e7c08d521 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -954,6 +954,21 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     let cloned_shared = Rc::clone(&cx.shared);
     let cache = &cloned_shared.cache;
     let mut extern_crates = FxHashSet::default();
+
+    if !t.is_object_safe(cx.tcx()) {
+        write_small_section_header(
+            w,
+            "object-safety",
+            "Object Safety",
+            &format!(
+                "<div class=\"object-safety-info\">This trait is <b>not</b> \
+                <a href=\"{base}/reference/items/traits.html#object-safety\">\
+                object safe</a>.</div>",
+                base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL
+            ),
+        );
+    }
+
     if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) {
         // The DefId is for the first Type found with that name. The bool is
         // if any Types with the same name but different DefId have been found.
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 4e8d88c55b6..ba4aaaff5a7 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -218,6 +218,14 @@ fn sidebar_trait<'a>(
     .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items))
     .collect();
     sidebar_assoc_items(cx, it, &mut blocks);
+
+    if !t.is_object_safe(cx.tcx()) {
+        blocks.push(LinkBlock::forced(
+            Link::new("object-safety", "Object Safety"),
+            "object-safety-note",
+        ));
+    }
+
     blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
     if t.is_auto(cx.tcx()) {
         blocks.push(LinkBlock::forced(
diff --git a/tests/rustdoc/sidebar-items.rs b/tests/rustdoc/sidebar-items.rs
index 6f7afa59bdd..b746f698264 100644
--- a/tests/rustdoc/sidebar-items.rs
+++ b/tests/rustdoc/sidebar-items.rs
@@ -14,6 +14,7 @@
 // @has - '//*[@class="sidebar-elems"]//section//a' 'Output'
 // @has - '//div[@class="sidebar-elems"]//h3/a[@href="#provided-associated-types"]' 'Provided Associated Types'
 // @has - '//*[@class="sidebar-elems"]//section//a' 'Extra'
+// @has - '//div[@class="sidebar-elems"]//h3/a[@href="#object-safety"]' 'Object Safety'
 pub trait Foo {
     const FOO: usize;
     const BAR: u32 = 0;
@@ -24,6 +25,12 @@ pub trait Foo {
     fn bar() -> Self::Output;
 }
 
+// @has foo/trait.Safe.html
+// @!has - '//div[@class="sidebar-elems"]//h3/a[@href="#object-safety"]' ''
+pub trait Safe {
+    fn access(&self);
+}
+
 // @has foo/struct.Bar.html
 // @has - '//div[@class="sidebar-elems"]//h3/a[@href="#fields"]' 'Fields'
 // @has - '//*[@class="sidebar-elems"]//section//a[@href="#structfield.f"]' 'f'
diff --git a/tests/rustdoc/trait-object-safe.rs b/tests/rustdoc/trait-object-safe.rs
new file mode 100644
index 00000000000..818843f7558
--- /dev/null
+++ b/tests/rustdoc/trait-object-safe.rs
@@ -0,0 +1,27 @@
+#![crate_name = "foo"]
+
+// @has 'foo/trait.Unsafe.html'
+// @has - '//*[@class="object-safety-info"]' 'This trait is not object safe.'
+// @has - '//*[@id="object-safety"]' 'Object Safety'
+pub trait Unsafe {
+    fn foo() -> Self;
+}
+
+// @has 'foo/trait.Unsafe2.html'
+// @has - '//*[@class="object-safety-info"]' 'This trait is not object safe.'
+// @has - '//*[@id="object-safety"]' 'Object Safety'
+pub trait Unsafe2<T> {
+    fn foo(i: T);
+}
+
+// @has 'foo/trait.Safe.html'
+// @!has - '//*[@class="object-safety-info"]' ''
+// @!has - '//*[@id="object-safety"]' ''
+pub trait Safe {
+    fn foo(&self);
+}
+
+// @has 'foo/struct.Foo.html'
+// @!has - '//*[@class="object-safety-info"]' ''
+// @!has - '//*[@id="object-safety"]' ''
+pub struct Foo;
diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs
new file mode 100644
index 00000000000..de5148bb5f4
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs
@@ -0,0 +1,148 @@
+// run-pass
+//! Sanity check Stable MIR Visitor
+
+// ignore-stage1
+// ignore-cross-compile
+// ignore-remote
+// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
+// edition: 2021
+
+#![feature(rustc_private)]
+#![feature(assert_matches)]
+#![feature(control_flow_enum)]
+
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use std::collections::HashSet;
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::rustc_internal;
+use stable_mir::*;
+use stable_mir::mir::MirVisitor;
+use std::io::Write;
+use std::ops::ControlFlow;
+
+const CRATE_NAME: &str = "input";
+
+fn test_visitor(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
+    let main_fn = stable_mir::entry_fn();
+    let main_body = main_fn.unwrap().body();
+    let main_visitor = TestVisitor::collect(&main_body);
+    assert!(main_visitor.ret_val.is_some());
+    assert!(main_visitor.args.is_empty());
+    assert!(main_visitor.tys.contains(&main_visitor.ret_val.unwrap().ty));
+    assert!(!main_visitor.calls.is_empty());
+
+    let exit_fn = main_visitor.calls.last().unwrap();
+    assert!(exit_fn.mangled_name().contains("exit_fn"), "Unexpected last function: {exit_fn:?}");
+
+    let exit_body = exit_fn.body();
+    let exit_visitor = TestVisitor::collect(&exit_body);
+    assert!(exit_visitor.ret_val.is_some());
+    assert_eq!(exit_visitor.args.len(), 1);
+    assert!(exit_visitor.tys.contains(&exit_visitor.ret_val.unwrap().ty));
+    assert!(exit_visitor.tys.contains(&exit_visitor.args[0].ty));
+    ControlFlow::Continue(())
+}
+
+struct TestVisitor<'a> {
+    pub body: &'a mir::Body,
+    pub tys: HashSet<ty::Ty>,
+    pub ret_val: Option<mir::LocalDecl>,
+    pub args: Vec<mir::LocalDecl>,
+    pub calls: Vec<mir::mono::Instance>
+}
+
+impl<'a> TestVisitor<'a> {
+    fn collect(body: &'a mir::Body) -> TestVisitor<'a> {
+        let mut visitor = TestVisitor {
+            body: &body,
+            tys: Default::default(),
+            ret_val: None,
+            args: vec![],
+            calls: vec![],
+        };
+        visitor.visit_body(&body);
+        visitor
+    }
+}
+
+impl<'a> mir::MirVisitor for TestVisitor<'a> {
+    fn visit_ty(&mut self, ty: &ty::Ty, _location: mir::visit::Location) {
+        self.tys.insert(*ty);
+        self.super_ty(ty)
+    }
+
+    fn visit_ret_decl(&mut self, local: mir::Local, decl: &mir::LocalDecl) {
+        assert!(local == mir::RETURN_LOCAL);
+        assert!(self.ret_val.is_none());
+        self.ret_val = Some(decl.clone());
+        self.super_ret_decl(local, decl);
+    }
+
+    fn visit_arg_decl(&mut self, local: mir::Local, decl: &mir::LocalDecl) {
+        self.args.push(decl.clone());
+        assert_eq!(local, self.args.len());
+        self.super_arg_decl(local, decl);
+    }
+
+    fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) {
+        if let mir::TerminatorKind::Call { func, .. } = &term.kind {
+            let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).kind() else { unreachable!
+            () };
+            let ty::RigidTy::FnDef(def, args) = ty else { unreachable!() };
+            self.calls.push(mir::mono::Instance::resolve(def, &args).unwrap());
+        }
+        self.super_terminator(term, location);
+    }
+}
+
+/// This test will generate and analyze a dummy crate using the stable mir.
+/// For that, it will first write the dummy crate into a file.
+/// Then it will create a `StableMir` using custom arguments and then
+/// it will run the compiler.
+fn main() {
+    let path = "sim_visitor_input.rs";
+    generate_input(&path).unwrap();
+    let args = vec![
+        "rustc".to_string(),
+        "-Cpanic=abort".to_string(),
+        "--crate-name".to_string(),
+        CRATE_NAME.to_string(),
+        path.to_string(),
+    ];
+    run!(args, tcx, test_visitor(tcx)).unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+    let mut file = std::fs::File::create(path)?;
+    write!(
+        file,
+        r#"
+    fn main() -> std::process::ExitCode {{
+        let inputs = Inputs::new();
+        let total = inputs.values.iter().sum();
+        exit_fn(total)
+    }}
+
+    fn exit_fn(code: u8) -> std::process::ExitCode {{
+        std::process::ExitCode::from(code)
+    }}
+
+    struct Inputs {{
+        values: [u8; 3],
+    }}
+
+    impl Inputs {{
+        fn new() -> Inputs {{
+            Inputs {{ values: [0, 1, 2] }}
+        }}
+    }}
+    "#
+    )?;
+    Ok(())
+}