diff --git a/Cargo.lock b/Cargo.lock
index 197b2c8f3f06a..39de549c7bd15 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4454,6 +4454,7 @@ dependencies = [
  "rustc_hir",
  "rustc_index",
  "rustc_infer",
+ "rustc_lint_defs",
  "rustc_macros",
  "rustc_middle",
  "rustc_parse_format",
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 06e9d9ed32933..bce5cd8174d08 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -279,6 +279,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
 
                 gate_doc!(
                     cfg => doc_cfg
+                    cfg_hide => doc_cfg_hide
                     masked => doc_masked
                     notable_trait => doc_notable_trait
                     keyword => doc_keyword
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index 4333038a6f936..97daad201d959 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -31,7 +31,7 @@ pub fn get_body_with_borrowck_facts<'tcx>(
     def: ty::WithOptConstParam<LocalDefId>,
 ) -> BodyWithBorrowckFacts<'tcx> {
     let (input_body, promoted) = tcx.mir_promoted(def);
-    tcx.infer_ctxt().enter(|infcx| {
+    tcx.infer_ctxt().with_opaque_type_inference(def.did).enter(|infcx| {
         let input_body: &Body<'_> = &input_body.borrow();
         let promoted: &IndexVec<_, _> = &promoted.borrow();
         *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap()
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 22dc8d101c844..c43141c769519 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -20,7 +20,7 @@ use rustc_middle::ty::{self, Ty};
 use rustc_middle::{bug, span_bug};
 use rustc_span::{sym, symbol::kw, Span, Symbol};
 use rustc_target::abi::{self, HasDataLayout, Primitive};
-use rustc_target::spec::PanicStrategy;
+use rustc_target::spec::{HasTargetSpec, PanicStrategy};
 
 use std::cmp::Ordering;
 use std::iter;
@@ -1190,11 +1190,28 @@ fn generic_simd_intrinsic(
     // FIXME: use:
     //  https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182
     //  https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81
-    fn llvm_vector_str(elem_ty: Ty<'_>, vec_len: u64, no_pointers: usize) -> String {
+    fn llvm_vector_str(
+        elem_ty: Ty<'_>,
+        vec_len: u64,
+        no_pointers: usize,
+        bx: &Builder<'a, 'll, 'tcx>,
+    ) -> String {
         let p0s: String = "p0".repeat(no_pointers);
         match *elem_ty.kind() {
-            ty::Int(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()),
-            ty::Uint(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()),
+            ty::Int(v) => format!(
+                "v{}{}i{}",
+                vec_len,
+                p0s,
+                // Normalize to prevent crash if v: IntTy::Isize
+                v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
+            ),
+            ty::Uint(v) => format!(
+                "v{}{}i{}",
+                vec_len,
+                p0s,
+                // Normalize to prevent crash if v: UIntTy::Usize
+                v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
+            ),
             ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()),
             _ => unreachable!(),
         }
@@ -1330,11 +1347,11 @@ fn generic_simd_intrinsic(
 
         // Type of the vector of pointers:
         let llvm_pointer_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count);
-        let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count);
+        let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count, bx);
 
         // Type of the vector of elements:
         let llvm_elem_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count - 1);
-        let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1);
+        let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1, bx);
 
         let llvm_intrinsic =
             format!("llvm.masked.gather.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str);
@@ -1458,11 +1475,11 @@ fn generic_simd_intrinsic(
 
         // Type of the vector of pointers:
         let llvm_pointer_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count);
-        let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count);
+        let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count, bx);
 
         // Type of the vector of elements:
         let llvm_elem_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count - 1);
-        let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1);
+        let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1, bx);
 
         let llvm_intrinsic =
             format!("llvm.masked.scatter.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str);
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index eae7f7854ecb1..ec2c703ad495d 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -675,6 +675,9 @@ declare_features! (
     /// Allows `#[track_caller]` on closures and generators.
     (active, closure_track_caller, "1.57.0", Some(87417), None),
 
+    /// Allows `#[doc(cfg_hide(...))]`.
+    (active, doc_cfg_hide, "1.57.0", Some(43781), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 9d56bc193fb29..7a51e1e321a2a 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3051,6 +3051,7 @@ declare_lint_pass! {
         BREAK_WITH_LABEL_AND_LOOP,
         UNUSED_ATTRIBUTES,
         NON_EXHAUSTIVE_OMITTED_PATTERNS,
+        DEREF_INTO_DYN_SUPERTRAIT,
     ]
 }
 
@@ -3512,3 +3513,48 @@ declare_lint! {
     Allow,
     "detect when patterns of types marked `non_exhaustive` are missed",
 }
+
+declare_lint! {
+    /// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the
+    /// `Deref` implementation with a `dyn SuperTrait` type as `Output`.
+    ///
+    /// These implementations will become shadowed when the `trait_upcasting` feature is stablized.
+    /// The `deref` functions will no longer be called implicitly, so there might be behavior change.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(deref_into_dyn_supertrait)]
+    /// #![allow(dead_code)]
+    ///
+    /// use core::ops::Deref;
+    ///
+    /// trait A {}
+    /// trait B: A {}
+    /// impl<'a> Deref for dyn 'a + B {
+    ///     type Target = dyn A;
+    ///     fn deref(&self) -> &Self::Target {
+    ///         todo!()
+    ///     }
+    /// }
+    ///
+    /// fn take_a(_: &dyn A) { }
+    ///
+    /// fn take_b(b: &dyn B) {
+    ///     take_a(b);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The dyn upcasting coercion feature adds new coercion rules, taking priority
+    /// over certain other coercion rules, which will cause some behavior change.
+    pub DEREF_INTO_DYN_SUPERTRAIT,
+    Warn,
+    "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>",
+    };
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 9850f395a0f65..97114729c0a27 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -54,7 +54,11 @@ static LLVM_THREAD_LOCAL char *LastError;
 //
 // Notably it exits the process with code 101, unlike LLVM's default of 1.
 static void FatalErrorHandler(void *UserData,
+#if LLVM_VERSION_LT(14, 0)
                               const std::string& Reason,
+#else
+                              const char* Reason,
+#endif
                               bool GenCrashDiag) {
   // Do the same thing that the default error handler does.
   std::cerr << "LLVM ERROR: " << Reason << std::endl;
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index e7b2a018680ad..1815302101f0e 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -938,6 +938,7 @@ impl CheckAttrVisitor<'tcx> {
                         // plugins: removed, but rustdoc warns about it itself
                         sym::alias
                         | sym::cfg
+                        | sym::cfg_hide
                         | sym::hidden
                         | sym::html_favicon_url
                         | sym::html_logo_url
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 77baf7d73810e..382dbc377d63e 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -399,6 +399,7 @@ symbols! {
         cfg_attr_multi,
         cfg_doctest,
         cfg_eval,
+        cfg_hide,
         cfg_panic,
         cfg_sanitize,
         cfg_target_abi,
@@ -547,6 +548,7 @@ symbols! {
         doc,
         doc_alias,
         doc_cfg,
+        doc_cfg_hide,
         doc_keyword,
         doc_masked,
         doc_notable_trait,
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index aa1074709a0e1..d59bdae0332cb 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_infer = { path = "../rustc_infer" }
+rustc_lint_defs = { path = "../rustc_lint_defs" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_session = { path = "../rustc_session" }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 719412492f637..f3706aa6e71aa 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -6,12 +6,17 @@
 //!
 //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
 use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_infer::traits::TraitEngine;
 use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
+use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable, WithConstness};
 use rustc_target::spec::abi::Abi;
 
+use crate::traits;
 use crate::traits::coherence::Conflict;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::{util, SelectionResult};
 use crate::traits::{Overflow, Unimplemented};
 
@@ -672,6 +677,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         })
     }
 
+    /// Temporary migration for #89190
+    fn need_migrate_deref_output_trait_object(
+        &mut self,
+        ty: Ty<'tcx>,
+        cause: &traits::ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<(Ty<'tcx>, DefId)> {
+        let tcx = self.tcx();
+        if tcx.features().trait_upcasting {
+            return None;
+        }
+
+        // <ty as Deref>
+        let trait_ref = ty::TraitRef {
+            def_id: tcx.lang_items().deref_trait()?,
+            substs: tcx.mk_substs_trait(ty, &[]),
+        };
+
+        let obligation = traits::Obligation::new(
+            cause.clone(),
+            param_env,
+            ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx),
+        );
+        if !self.infcx.predicate_may_hold(&obligation) {
+            return None;
+        }
+
+        let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
+        let normalized_ty = fulfillcx.normalize_projection_type(
+            &self.infcx,
+            param_env,
+            ty::ProjectionTy {
+                item_def_id: tcx.lang_items().deref_target()?,
+                substs: trait_ref.substs,
+            },
+            cause.clone(),
+        );
+
+        let data = if let ty::Dynamic(ref data, ..) = normalized_ty.kind() {
+            data
+        } else {
+            return None;
+        };
+
+        let def_id = data.principal_def_id()?;
+
+        return Some((normalized_ty, def_id));
+    }
+
     /// Searches for unsizing that might apply to `obligation`.
     fn assemble_candidates_for_unsizing(
         &mut self,
@@ -732,6 +786,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         let principal_a = data_a.principal().unwrap();
                         let target_trait_did = principal_def_id_b.unwrap();
                         let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
+                        if let Some((deref_output_ty, deref_output_trait_did)) = self
+                            .need_migrate_deref_output_trait_object(
+                                source,
+                                &obligation.cause,
+                                obligation.param_env,
+                            )
+                        {
+                            if deref_output_trait_did == target_trait_did {
+                                self.tcx().struct_span_lint_hir(
+                                    DEREF_INTO_DYN_SUPERTRAIT,
+                                    obligation.cause.body_id,
+                                    obligation.cause.span,
+                                    |lint| {
+                                        lint.build(&format!(
+                                            "`{}` implements `Deref` with supertrait `{}` as output",
+                                            source,
+                                            deref_output_ty
+                                        )).emit();
+                                    },
+                                );
+                                return;
+                            }
+                        }
+
                         for (idx, upcast_trait_ref) in
                             util::supertraits(self.tcx(), source_trait_ref).enumerate()
                         {
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 2e607de1d3371..cee3679d0a052 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -752,29 +752,31 @@ fn infer_placeholder_type<'a>(
     // us to improve in typeck so we do that now.
     match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) {
         Some(mut err) => {
-            // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
-            // We are typeck and have the real type, so remove that and suggest the actual type.
-            err.suggestions.clear();
-
-            // Suggesting unnameable types won't help.
-            let mut mk_nameable = MakeNameable::new(tcx);
-            let ty = mk_nameable.fold_ty(ty);
-            let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
-            if let Some(sugg_ty) = sugg_ty {
-                err.span_suggestion(
-                    span,
-                    &format!("provide a type for the {item}", item = kind),
-                    format!("{}: {}", item_ident, sugg_ty),
-                    Applicability::MachineApplicable,
-                );
-            } else {
-                err.span_note(
-                    tcx.hir().body(body_id).value.span,
-                    &format!("however, the inferred type `{}` cannot be named", ty.to_string()),
-                );
+            if !ty.references_error() {
+                // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
+                // We are typeck and have the real type, so remove that and suggest the actual type.
+                err.suggestions.clear();
+
+                // Suggesting unnameable types won't help.
+                let mut mk_nameable = MakeNameable::new(tcx);
+                let ty = mk_nameable.fold_ty(ty);
+                let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
+                if let Some(sugg_ty) = sugg_ty {
+                    err.span_suggestion(
+                        span,
+                        &format!("provide a type for the {item}", item = kind),
+                        format!("{}: {}", item_ident, sugg_ty),
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    err.span_note(
+                        tcx.hir().body(body_id).value.span,
+                        &format!("however, the inferred type `{}` cannot be named", ty.to_string()),
+                    );
+                }
             }
 
-            err.emit_unless(ty.references_error());
+            err.emit();
         }
         None => {
             let mut diag = bad_placeholder_type(tcx, vec![span], kind);
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index ca41ce975e4fe..72fe84222deac 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -67,6 +67,10 @@
     issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
     test(no_crate_inject, attr(allow(unused_variables), deny(warnings)))
 )]
+#![cfg_attr(
+    not(bootstrap),
+    doc(cfg_hide(not(test), not(any(test, bootstrap)), target_has_atomic = "ptr"))
+)]
 #![no_std]
 #![needs_allocator]
 #![warn(deprecated_in_future)]
@@ -146,6 +150,8 @@
 #![feature(associated_type_bounds)]
 #![feature(slice_group_by)]
 #![feature(decl_macro)]
+#![feature(doc_cfg)]
+#![cfg_attr(not(bootstrap), feature(doc_cfg_hide))]
 // Allow testing this library
 
 #[cfg(test)]
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index f9d517ce8c0eb..630876445ba82 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -60,6 +60,30 @@
     test(no_crate_inject, attr(deny(warnings))),
     test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
 )]
+#![cfg_attr(
+    not(bootstrap),
+    doc(cfg_hide(
+        not(test),
+        target_pointer_width = "16",
+        target_pointer_width = "32",
+        target_pointer_width = "64",
+        target_has_atomic = "8",
+        target_has_atomic = "16",
+        target_has_atomic = "32",
+        target_has_atomic = "64",
+        target_has_atomic = "ptr",
+        target_has_atomic_equal_alignment = "8",
+        target_has_atomic_equal_alignment = "16",
+        target_has_atomic_equal_alignment = "32",
+        target_has_atomic_equal_alignment = "64",
+        target_has_atomic_equal_alignment = "ptr",
+        target_has_atomic_load_store = "8",
+        target_has_atomic_load_store = "16",
+        target_has_atomic_load_store = "32",
+        target_has_atomic_load_store = "64",
+        target_has_atomic_load_store = "ptr",
+    ))
+)]
 #![no_core]
 //
 // Lints:
@@ -133,6 +157,7 @@
 #![feature(doc_notable_trait)]
 #![feature(doc_primitive)]
 #![feature(exhaustive_patterns)]
+#![cfg_attr(not(bootstrap), feature(doc_cfg_hide))]
 #![feature(extern_types)]
 #![feature(fundamental)]
 #![feature(if_let_guard)]
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index a7516bf4ffd37..b16436a18f0af 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -195,6 +195,7 @@
     test(no_crate_inject, attr(deny(warnings))),
     test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
 )]
+#![cfg_attr(not(bootstrap), doc(cfg_hide(not(test), not(any(test, bootstrap)))))]
 // Don't link to std. We are std.
 #![no_std]
 #![warn(deprecated_in_future)]
@@ -263,6 +264,7 @@
 #![feature(custom_test_frameworks)]
 #![feature(decl_macro)]
 #![feature(doc_cfg)]
+#![cfg_attr(not(bootstrap), feature(doc_cfg_hide))]
 #![feature(doc_keyword)]
 #![feature(doc_masked)]
 #![feature(doc_notable_trait)]
diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs
index 1e220ea30ab95..30eeac14b43f5 100644
--- a/library/std/src/os/raw/mod.rs
+++ b/library/std/src/os/raw/mod.rs
@@ -46,6 +46,7 @@ macro_rules! type_alias {
 }
 
 type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8;
+#[doc(cfg(all()))]
 #[cfg(any(
     all(
         target_os = "linux",
@@ -88,6 +89,7 @@ type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8;
     all(target_os = "fuchsia", target_arch = "aarch64")
 ))]}
 type_alias! { "char.md", c_char = i8, NonZero_c_char = NonZeroI8;
+#[doc(cfg(all()))]
 #[cfg(not(any(
     all(
         target_os = "linux",
@@ -136,12 +138,16 @@ type_alias! { "ushort.md", c_ushort = u16, NonZero_c_ushort = NonZeroU16; }
 type_alias! { "int.md", c_int = i32, NonZero_c_int = NonZeroI32; }
 type_alias! { "uint.md", c_uint = u32, NonZero_c_uint = NonZeroU32; }
 type_alias! { "long.md", c_long = i32, NonZero_c_long = NonZeroI32;
+#[doc(cfg(all()))]
 #[cfg(any(target_pointer_width = "32", windows))] }
 type_alias! { "ulong.md", c_ulong = u32, NonZero_c_ulong = NonZeroU32;
+#[doc(cfg(all()))]
 #[cfg(any(target_pointer_width = "32", windows))] }
 type_alias! { "long.md", c_long = i64, NonZero_c_long = NonZeroI64;
+#[doc(cfg(all()))]
 #[cfg(all(target_pointer_width = "64", not(windows)))] }
 type_alias! { "ulong.md", c_ulong = u64, NonZero_c_ulong = NonZeroU64;
+#[doc(cfg(all()))]
 #[cfg(all(target_pointer_width = "64", not(windows)))] }
 type_alias! { "longlong.md", c_longlong = i64, NonZero_c_longlong = NonZeroI64; }
 type_alias! { "ulonglong.md", c_ulonglong = u64, NonZero_c_ulonglong = NonZeroU64; }
diff --git a/library/std/src/os/windows/raw.rs b/library/std/src/os/windows/raw.rs
index 5014e008eb599..0ef3adade5c83 100644
--- a/library/std/src/os/windows/raw.rs
+++ b/library/std/src/os/windows/raw.rs
@@ -7,8 +7,10 @@ use crate::os::raw::c_void;
 #[stable(feature = "raw_ext", since = "1.1.0")]
 pub type HANDLE = *mut c_void;
 #[cfg(target_pointer_width = "32")]
+#[doc(cfg(all()))]
 #[stable(feature = "raw_ext", since = "1.1.0")]
 pub type SOCKET = u32;
 #[cfg(target_pointer_width = "64")]
+#[doc(cfg(all()))]
 #[stable(feature = "raw_ext", since = "1.1.0")]
 pub type SOCKET = u64;
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 4a888b22332ee..b463c1dc7146b 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -318,10 +318,10 @@ fn merge_attrs(
             } else {
                 Attributes::from_ast(&both, None)
             },
-            both.cfg(cx.sess()),
+            both.cfg(cx.tcx, &cx.cache.hidden_cfg),
         )
     } else {
-        (old_attrs.clean(cx), old_attrs.cfg(cx.sess()))
+        (old_attrs.clean(cx), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg))
     }
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index a55d85f5841d2..969d15dd6a160 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1973,7 +1973,7 @@ fn clean_extern_crate(
         def_id: crate_def_id.into(),
         visibility: krate.vis.clean(cx),
         kind: box ExternCrateItem { src: orig_name },
-        cfg: attrs.cfg(cx.sess()),
+        cfg: attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
     }]
 }
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 0e78fe7aec357..5b722175f988c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -421,7 +421,7 @@ impl Item {
             kind,
             box ast_attrs.clean(cx),
             cx,
-            ast_attrs.cfg(cx.sess()),
+            ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
         )
     }
 
@@ -747,7 +747,7 @@ crate trait AttributesExt {
 
     fn other_attrs(&self) -> Vec<ast::Attribute>;
 
-    fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>>;
+    fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>;
 }
 
 impl AttributesExt for [ast::Attribute] {
@@ -772,8 +772,41 @@ impl AttributesExt for [ast::Attribute] {
         self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect()
     }
 
-    fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>> {
-        let mut cfg = Cfg::True;
+    fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
+        let sess = tcx.sess;
+        let doc_cfg_active = tcx.features().doc_cfg;
+
+        fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
+            let mut iter = it.into_iter();
+            let item = iter.next()?;
+            if iter.next().is_some() {
+                return None;
+            }
+            Some(item)
+        }
+
+        let mut cfg = if doc_cfg_active {
+            let mut doc_cfg = self
+                .iter()
+                .filter(|attr| attr.has_name(sym::doc))
+                .flat_map(|attr| attr.meta_item_list().unwrap_or_else(Vec::new))
+                .filter(|attr| attr.has_name(sym::cfg))
+                .peekable();
+            if doc_cfg.peek().is_some() {
+                doc_cfg
+                    .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
+                    .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
+            } else {
+                self.iter()
+                    .filter(|attr| attr.has_name(sym::cfg))
+                    .filter_map(|attr| single(attr.meta_item_list()?))
+                    .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
+                    .filter(|cfg| !hidden_cfg.contains(cfg))
+                    .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
+            }
+        } else {
+            Cfg::True
+        };
 
         for attr in self.iter() {
             // #[doc]
@@ -800,6 +833,8 @@ impl AttributesExt for [ast::Attribute] {
             }
         }
 
+        // treat #[target_feature(enable = "feat")] attributes as if they were
+        // #[doc(cfg(target_feature = "feat"))] attributes as well
         for attr in self.lists(sym::target_feature) {
             if attr.has_name(sym::enable) {
                 if let Some(feat) = attr.value_str() {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 43abcf095d858..9e64d200b4373 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1123,7 +1123,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
         let ast_attrs = self.tcx.hir().attrs(hir_id);
         let mut attrs = Attributes::from_ast(ast_attrs, None);
 
-        if let Some(ref cfg) = ast_attrs.cfg(self.sess) {
+        if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
             if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
                 return;
             }
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index bcfcc3d70395c..cc9e081add199 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -119,6 +119,8 @@ crate struct Cache {
     ///
     /// Links are indexed by the DefId of the item they document.
     crate intra_doc_links: FxHashMap<ItemId, Vec<clean::ItemLink>>,
+    /// Cfg that have been hidden via #![doc(cfg_hide(...))]
+    crate hidden_cfg: FxHashSet<clean::cfg::Cfg>,
 }
 
 /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`.
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 1275fa4e15617..cab3fca708b2e 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -323,7 +323,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     let import_item = clean::Item {
                         def_id: import_def_id.into(),
                         attrs: import_attrs,
-                        cfg: ast_attrs.cfg(cx.sess()),
+                        cfg: ast_attrs.cfg(cx.tcx(), &cx.cache().hidden_cfg),
                         ..myitem.clone()
                     };
 
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 4f5495a176dfc..36b1a14f6c1ea 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -6,6 +6,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::Node;
+use rustc_hir::CRATE_HIR_ID;
 use rustc_middle::middle::privacy::AccessLevel;
 use rustc_middle::ty::TyCtxt;
 use rustc_span;
@@ -15,7 +16,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
 
 use std::mem;
 
-use crate::clean::{self, AttributesExt, NestedAttributesExt};
+use crate::clean::{self, cfg::Cfg, AttributesExt, NestedAttributesExt};
 use crate::core;
 use crate::doctree::*;
 
@@ -97,6 +98,31 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 }
             }
         }
+
+        self.cx.cache.hidden_cfg = self
+            .cx
+            .tcx
+            .hir()
+            .attrs(CRATE_HIR_ID)
+            .iter()
+            .filter(|attr| attr.has_name(sym::doc))
+            .flat_map(|attr| attr.meta_item_list().into_iter().flatten())
+            .filter(|attr| attr.has_name(sym::cfg_hide))
+            .flat_map(|attr| {
+                attr.meta_item_list()
+                    .unwrap_or(&[])
+                    .iter()
+                    .filter_map(|attr| {
+                        Some(
+                            Cfg::parse(attr.meta_item()?)
+                                .map_err(|e| self.cx.sess().diagnostic().span_err(e.span, e.msg))
+                                .ok()?,
+                        )
+                    })
+                    .collect::<Vec<_>>()
+            })
+            .collect();
+
         self.cx.cache.exact_paths = self.exact_paths;
         top_level_module
     }
diff --git a/src/test/rustdoc-ui/doc-cfg.rs b/src/test/rustdoc-ui/doc-cfg.rs
new file mode 100644
index 0000000000000..354d76bc3c433
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-cfg.rs
@@ -0,0 +1,9 @@
+#![feature(doc_cfg)]
+
+#[doc(cfg(), cfg(foo, bar))]
+//~^ ERROR
+//~^^ ERROR
+#[doc(cfg(foo), cfg(bar))] // ok!
+#[doc(cfg())] //~ ERROR
+#[doc(cfg(foo, bar))] //~ ERROR
+pub fn foo() {}
diff --git a/src/test/rustdoc-ui/doc-cfg.stderr b/src/test/rustdoc-ui/doc-cfg.stderr
new file mode 100644
index 0000000000000..b379f6febe29f
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-cfg.stderr
@@ -0,0 +1,26 @@
+error: `cfg` predicate is not specified
+  --> $DIR/doc-cfg.rs:3:7
+   |
+LL | #[doc(cfg(), cfg(foo, bar))]
+   |       ^^^^^
+
+error: multiple `cfg` predicates are specified
+  --> $DIR/doc-cfg.rs:3:23
+   |
+LL | #[doc(cfg(), cfg(foo, bar))]
+   |                       ^^^
+
+error: `cfg` predicate is not specified
+  --> $DIR/doc-cfg.rs:7:7
+   |
+LL | #[doc(cfg())]
+   |       ^^^^^
+
+error: multiple `cfg` predicates are specified
+  --> $DIR/doc-cfg.rs:8:16
+   |
+LL | #[doc(cfg(foo, bar))]
+   |                ^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.rs b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.rs
new file mode 100644
index 0000000000000..17812018b9b7a
--- /dev/null
+++ b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.rs
@@ -0,0 +1,7 @@
+#![doc(cfg_hide(test))]
+//~^ ERROR `#[doc(cfg_hide)]` is experimental
+
+#[cfg(not(test))]
+pub fn public_fn() {}
+#[cfg(test)]
+pub fn internal_use_only() {}
diff --git a/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.stderr b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.stderr
new file mode 100644
index 0000000000000..ba42c7bbb05bc
--- /dev/null
+++ b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.stderr
@@ -0,0 +1,14 @@
+error[E0658]: `#[doc(cfg_hide)]` is experimental
+  --> $DIR/feature-gate-doc_cfg_hide.rs:1:1
+   |
+LL | #![doc(cfg_hide(test))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg_hide)]` to the crate attributes to enable
+
+error: Compilation failed, aborting rustdoc
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/rustdoc/doc-cfg-hide.rs b/src/test/rustdoc/doc-cfg-hide.rs
new file mode 100644
index 0000000000000..b9d0d32313723
--- /dev/null
+++ b/src/test/rustdoc/doc-cfg-hide.rs
@@ -0,0 +1,32 @@
+#![crate_name = "oud"]
+#![feature(doc_cfg, doc_cfg_hide)]
+
+#![doc(cfg_hide(feature = "solecism"))]
+
+// @has 'oud/struct.Solecism.html'
+// @count   - '//*[@class="stab portability"]' 0
+// compile-flags:--cfg feature="solecism"
+#[cfg(feature = "solecism")]
+pub struct Solecism;
+
+// @has 'oud/struct.Scribacious.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature solecism'
+#[cfg(feature = "solecism")]
+#[doc(cfg(feature = "solecism"))]
+pub struct Scribacious;
+
+// @has 'oud/struct.Hyperdulia.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature hyperdulia'
+// compile-flags:--cfg feature="hyperdulia"
+#[cfg(feature = "solecism")]
+#[cfg(feature = "hyperdulia")]
+pub struct Hyperdulia;
+
+// @has 'oud/struct.Oystercatcher.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate features solecism and oystercatcher'
+// compile-flags:--cfg feature="oystercatcher"
+#[cfg(all(feature = "solecism", feature = "oystercatcher"))]
+pub struct Oystercatcher;
diff --git a/src/test/rustdoc/doc-cfg-implicit-gate.rs b/src/test/rustdoc/doc-cfg-implicit-gate.rs
new file mode 100644
index 0000000000000..92804d3729bba
--- /dev/null
+++ b/src/test/rustdoc/doc-cfg-implicit-gate.rs
@@ -0,0 +1,7 @@
+// compile-flags:--cfg feature="worricow"
+#![crate_name = "xenogenous"]
+
+// @has 'xenogenous/struct.Worricow.html'
+// @count   - '//*[@class="stab portability"]' 0
+#[cfg(feature = "worricow")]
+pub struct Worricow;
diff --git a/src/test/rustdoc/doc-cfg-implicit.rs b/src/test/rustdoc/doc-cfg-implicit.rs
new file mode 100644
index 0000000000000..36c2025785d0f
--- /dev/null
+++ b/src/test/rustdoc/doc-cfg-implicit.rs
@@ -0,0 +1,31 @@
+#![crate_name = "funambulism"]
+#![feature(doc_cfg)]
+
+// @has 'funambulism/struct.Disorbed.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature disorbed'
+// compile-flags:--cfg feature="disorbed"
+#[cfg(feature = "disorbed")]
+pub struct Disorbed;
+
+// @has 'funambulism/struct.Aesthesia.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature aesthesia'
+// compile-flags:--cfg feature="aesthesia"
+#[doc(cfg(feature = "aesthesia"))]
+pub struct Aesthesia;
+
+// @has 'funambulism/struct.Pliothermic.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature pliothermic'
+// compile-flags:--cfg feature="epopoeist"
+#[cfg(feature = "epopoeist")]
+#[doc(cfg(feature = "pliothermic"))]
+pub struct Pliothermic;
+
+// @has 'funambulism/struct.Simillimum.html'
+// @count   - '//*[@class="stab portability"]' 0
+// compile-flags:--cfg feature="simillimum"
+#[cfg(feature = "simillimum")]
+#[doc(cfg(all()))]
+pub struct Simillimum;
diff --git a/src/test/ui/parser/issue-89574.rs b/src/test/ui/parser/issue-89574.rs
new file mode 100644
index 0000000000000..0a477f1aa5fb6
--- /dev/null
+++ b/src/test/ui/parser/issue-89574.rs
@@ -0,0 +1,4 @@
+fn main() {
+    const EMPTY_ARRAY = [];
+    //~^ missing type for `const` item
+}
diff --git a/src/test/ui/parser/issue-89574.stderr b/src/test/ui/parser/issue-89574.stderr
new file mode 100644
index 0000000000000..cbee3d35155c7
--- /dev/null
+++ b/src/test/ui/parser/issue-89574.stderr
@@ -0,0 +1,8 @@
+error: missing type for `const` item
+  --> $DIR/issue-89574.rs:2:11
+   |
+LL |     const EMPTY_ARRAY = [];
+   |           ^^^^^^^^^^^ help: provide a type for the item: `EMPTY_ARRAY: <type>`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs b/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs
index 15a15a207b1ca..613b3c9856171 100644
--- a/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs
+++ b/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs
@@ -4,3 +4,4 @@ fn main() {}
 
 const A: u8; //~ ERROR free constant item without body
 const B; //~ ERROR free constant item without body
+//~^ ERROR missing type for `const` item
diff --git a/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr b/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr
index aa75e5cee01d4..c340e958ee585 100644
--- a/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr
+++ b/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr
@@ -14,5 +14,11 @@ LL | const B;
    |        |
    |        help: provide a definition for the constant: `= <expr>;`
 
-error: aborting due to 2 previous errors
+error: missing type for `const` item
+  --> $DIR/item-free-const-no-body-semantic-fail.rs:6:7
+   |
+LL | const B;
+   |       ^ help: provide a type for the item: `B: <type>`
+
+error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs b/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs
index 61d3eab24d8c7..780479e3d26ac 100644
--- a/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs
+++ b/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs
@@ -4,6 +4,8 @@ fn main() {}
 
 static A: u8; //~ ERROR free static item without body
 static B; //~ ERROR free static item without body
+//~^ ERROR missing type for `static` item
 
 static mut C: u8; //~ ERROR free static item without body
 static mut D; //~ ERROR free static item without body
+//~^ ERROR missing type for `static mut` item
diff --git a/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr b/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr
index 7b408323674de..4d542b79861fd 100644
--- a/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr
+++ b/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr
@@ -15,7 +15,7 @@ LL | static B;
    |         help: provide a definition for the static: `= <expr>;`
 
 error: free static item without body
-  --> $DIR/item-free-static-no-body-semantic-fail.rs:8:1
+  --> $DIR/item-free-static-no-body-semantic-fail.rs:9:1
    |
 LL | static mut C: u8;
    | ^^^^^^^^^^^^^^^^-
@@ -23,12 +23,24 @@ LL | static mut C: u8;
    |                 help: provide a definition for the static: `= <expr>;`
 
 error: free static item without body
-  --> $DIR/item-free-static-no-body-semantic-fail.rs:9:1
+  --> $DIR/item-free-static-no-body-semantic-fail.rs:10:1
    |
 LL | static mut D;
    | ^^^^^^^^^^^^-
    |             |
    |             help: provide a definition for the static: `= <expr>;`
 
-error: aborting due to 4 previous errors
+error: missing type for `static` item
+  --> $DIR/item-free-static-no-body-semantic-fail.rs:6:8
+   |
+LL | static B;
+   |        ^ help: provide a type for the item: `B: <type>`
+
+error: missing type for `static mut` item
+  --> $DIR/item-free-static-no-body-semantic-fail.rs:10:12
+   |
+LL | static mut D;
+   |            ^ help: provide a type for the item: `D: <type>`
+
+error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/simd/issue-89193.rs b/src/test/ui/simd/issue-89193.rs
new file mode 100644
index 0000000000000..79c4e6a312c0d
--- /dev/null
+++ b/src/test/ui/simd/issue-89193.rs
@@ -0,0 +1,51 @@
+// run-pass
+
+// Test that simd gather instructions on slice of usize don't cause crash
+// See issue #89183 - https://github.com/rust-lang/rust/issues/89193
+
+#![feature(repr_simd, platform_intrinsics)]
+#![allow(non_camel_case_types)]
+
+#[repr(simd)]
+#[derive(Copy, Clone, PartialEq, Debug)]
+struct x4<T>(pub T, pub T, pub T, pub T);
+
+extern "platform-intrinsic" {
+    fn simd_gather<T, U, V>(x: T, y: U, z: V) -> T;
+}
+
+fn main() {
+    let x: [usize; 4] = [10, 11, 12, 13];
+    let default = x4(0_usize, 1, 2, 3);
+    let mask = x4(1_i32, 1, 1, 1);
+    let expected = x4(10_usize, 11, 12, 13);
+
+    unsafe {
+        let pointer = &x[0] as *const usize;
+        let pointers =  x4(
+            pointer.offset(0) as *const usize,
+            pointer.offset(1),
+            pointer.offset(2),
+            pointer.offset(3)
+        );
+        let result = simd_gather(default, pointers, mask);
+        assert_eq!(result, expected);
+    }
+
+    // and again for isize
+    let x: [isize; 4] = [10, 11, 12, 13];
+    let default = x4(0_isize, 1, 2, 3);
+    let expected = x4(10_isize, 11, 12, 13);
+
+    unsafe {
+        let pointer = &x[0] as *const isize;
+        let pointers =  x4(
+            pointer.offset(0) as *const isize,
+            pointer.offset(1),
+            pointer.offset(2),
+            pointer.offset(3)
+        );
+        let result = simd_gather(default, pointers, mask);
+        assert_eq!(result, expected);
+    }
+}
diff --git a/src/test/ui/traits/trait-upcasting/migrate-lint-deny.rs b/src/test/ui/traits/trait-upcasting/migrate-lint-deny.rs
new file mode 100644
index 0000000000000..c6725101858eb
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/migrate-lint-deny.rs
@@ -0,0 +1,25 @@
+#![deny(deref_into_dyn_supertrait)]
+
+extern crate core;
+
+use core::ops::Deref;
+
+// issue 89190
+trait A {}
+trait B: A {}
+impl<'a> Deref for dyn 'a + B {
+    type Target = dyn A;
+    fn deref(&self) -> &Self::Target {
+        todo!()
+    }
+}
+
+fn take_a(_: &dyn A) {}
+
+fn whoops(b: &dyn B) {
+    take_a(b)
+    //~^ ERROR `dyn B` implements `Deref` with supertrait `(dyn A + 'static)` as output
+    //~^^ WARN this was previously accepted by the compiler but is being phased out;
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/trait-upcasting/migrate-lint-deny.stderr b/src/test/ui/traits/trait-upcasting/migrate-lint-deny.stderr
new file mode 100644
index 0000000000000..35af9112a27fc
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/migrate-lint-deny.stderr
@@ -0,0 +1,16 @@
+error: `dyn B` implements `Deref` with supertrait `(dyn A + 'static)` as output
+  --> $DIR/migrate-lint-deny.rs:20:12
+   |
+LL |     take_a(b)
+   |            ^
+   |
+note: the lint level is defined here
+  --> $DIR/migrate-lint-deny.rs:1:9
+   |
+LL | #![deny(deref_into_dyn_supertrait)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #89460 <https://github.com/rust-lang/rust/issues/89460>
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/typeck/issue-79040.rs b/src/test/ui/typeck/issue-79040.rs
index af2a9c1ba87ef..941612542207c 100644
--- a/src/test/ui/typeck/issue-79040.rs
+++ b/src/test/ui/typeck/issue-79040.rs
@@ -1,5 +1,5 @@
 fn main() {
     const FOO = "hello" + 1; //~ ERROR cannot add `{integer}` to `&str`
-    //~^ ERROR cannot add `{integer}` to `&str`
+    //~^ missing type for `const` item
     println!("{}", FOO);
 }
diff --git a/src/test/ui/typeck/issue-79040.stderr b/src/test/ui/typeck/issue-79040.stderr
index 32049e5d96860..aec2e1ec9e4ef 100644
--- a/src/test/ui/typeck/issue-79040.stderr
+++ b/src/test/ui/typeck/issue-79040.stderr
@@ -6,13 +6,11 @@ LL |     const FOO = "hello" + 1;
    |                 |
    |                 &str
 
-error[E0369]: cannot add `{integer}` to `&str`
-  --> $DIR/issue-79040.rs:2:25
+error: missing type for `const` item
+  --> $DIR/issue-79040.rs:2:11
    |
 LL |     const FOO = "hello" + 1;
-   |                 ------- ^ - {integer}
-   |                 |
-   |                 &str
+   |           ^^^ help: provide a type for the item: `FOO: <type>`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/compiletest/src/compute_diff.rs b/src/tools/compiletest/src/compute_diff.rs
new file mode 100644
index 0000000000000..92c80c27de03b
--- /dev/null
+++ b/src/tools/compiletest/src/compute_diff.rs
@@ -0,0 +1,157 @@
+use std::collections::VecDeque;
+use std::fs::{File, FileType};
+use std::path::Path;
+
+#[derive(Debug, PartialEq)]
+pub enum DiffLine {
+    Context(String),
+    Expected(String),
+    Resulting(String),
+}
+
+#[derive(Debug, PartialEq)]
+pub struct Mismatch {
+    pub line_number: u32,
+    pub lines: Vec<DiffLine>,
+}
+
+impl Mismatch {
+    fn new(line_number: u32) -> Mismatch {
+        Mismatch { line_number, lines: Vec::new() }
+    }
+}
+
+// Produces a diff between the expected output and actual output.
+pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
+    let mut line_number = 1;
+    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
+    let mut lines_since_mismatch = context_size + 1;
+    let mut results = Vec::new();
+    let mut mismatch = Mismatch::new(0);
+
+    for result in diff::lines(expected, actual) {
+        match result {
+            diff::Result::Left(str) => {
+                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
+                    results.push(mismatch);
+                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
+                }
+
+                while let Some(line) = context_queue.pop_front() {
+                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
+                }
+
+                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
+                line_number += 1;
+                lines_since_mismatch = 0;
+            }
+            diff::Result::Right(str) => {
+                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
+                    results.push(mismatch);
+                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
+                }
+
+                while let Some(line) = context_queue.pop_front() {
+                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
+                }
+
+                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
+                lines_since_mismatch = 0;
+            }
+            diff::Result::Both(str, _) => {
+                if context_queue.len() >= context_size {
+                    let _ = context_queue.pop_front();
+                }
+
+                if lines_since_mismatch < context_size {
+                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
+                } else if context_size > 0 {
+                    context_queue.push_back(str);
+                }
+
+                line_number += 1;
+                lines_since_mismatch += 1;
+            }
+        }
+    }
+
+    results.push(mismatch);
+    results.remove(0);
+
+    results
+}
+
+pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
+    use std::fmt::Write;
+    let mut output = String::new();
+    let diff_results = make_diff(expected, actual, context_size);
+    for result in diff_results {
+        let mut line_number = result.line_number;
+        for line in result.lines {
+            match line {
+                DiffLine::Expected(e) => {
+                    writeln!(output, "-\t{}", e).unwrap();
+                    line_number += 1;
+                }
+                DiffLine::Context(c) => {
+                    writeln!(output, "{}\t{}", line_number, c).unwrap();
+                    line_number += 1;
+                }
+                DiffLine::Resulting(r) => {
+                    writeln!(output, "+\t{}", r).unwrap();
+                }
+            }
+        }
+        writeln!(output).unwrap();
+    }
+    output
+}
+
+/// Filters based on filetype and extension whether to diff a file.
+///
+/// Returns whether any data was actually written.
+pub(crate) fn write_filtered_diff<Filter>(
+    diff_filename: &str,
+    out_dir: &Path,
+    compare_dir: &Path,
+    verbose: bool,
+    filter: Filter,
+) -> bool
+where
+    Filter: Fn(FileType, Option<&str>) -> bool,
+{
+    use std::io::{Read, Write};
+    let mut diff_output = File::create(diff_filename).unwrap();
+    let mut wrote_data = false;
+    for entry in walkdir::WalkDir::new(out_dir) {
+        let entry = entry.expect("failed to read file");
+        let extension = entry.path().extension().and_then(|p| p.to_str());
+        if filter(entry.file_type(), extension) {
+            let expected_path = compare_dir.join(entry.path().strip_prefix(&out_dir).unwrap());
+            let expected = if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
+            let actual_path = entry.path();
+            let actual = std::fs::read(&actual_path).unwrap();
+            let diff = unified_diff::diff(
+                &expected,
+                &expected_path.to_string_lossy(),
+                &actual,
+                &actual_path.to_string_lossy(),
+                3,
+            );
+            wrote_data |= !diff.is_empty();
+            diff_output.write_all(&diff).unwrap();
+        }
+    }
+
+    if !wrote_data {
+        println!("note: diff is identical to nightly rustdoc");
+        assert!(diff_output.metadata().unwrap().len() == 0);
+        return false;
+    } else if verbose {
+        eprintln!("printing diff:");
+        let mut buf = Vec::new();
+        diff_output.read_to_end(&mut buf).unwrap();
+        std::io::stderr().lock().write_all(&mut buf).unwrap();
+    }
+    true
+}
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 9e655557fd271..fbf3249db94d1 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -28,6 +28,7 @@ use self::header::{make_test_description, EarlyProps};
 mod tests;
 
 pub mod common;
+pub mod compute_diff;
 pub mod errors;
 pub mod header;
 mod json;
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 2a4bb9eb88b30..0821e279d2485 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -8,6 +8,7 @@ use crate::common::{CompareMode, FailMode, PassMode};
 use crate::common::{Config, TestPaths};
 use crate::common::{Pretty, RunPassValgrind};
 use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT};
+use crate::compute_diff::{write_diff, write_filtered_diff};
 use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::json;
@@ -18,7 +19,7 @@ use regex::{Captures, Regex};
 use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
 
 use std::collections::hash_map::DefaultHasher;
-use std::collections::{HashMap, HashSet, VecDeque};
+use std::collections::{HashMap, HashSet};
 use std::env;
 use std::ffi::{OsStr, OsString};
 use std::fs::{self, create_dir_all, File, OpenOptions};
@@ -100,111 +101,6 @@ pub fn get_lib_name(lib: &str, dylib: bool) -> String {
     }
 }
 
-#[derive(Debug, PartialEq)]
-pub enum DiffLine {
-    Context(String),
-    Expected(String),
-    Resulting(String),
-}
-
-#[derive(Debug, PartialEq)]
-pub struct Mismatch {
-    pub line_number: u32,
-    pub lines: Vec<DiffLine>,
-}
-
-impl Mismatch {
-    fn new(line_number: u32) -> Mismatch {
-        Mismatch { line_number, lines: Vec::new() }
-    }
-}
-
-// Produces a diff between the expected output and actual output.
-pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
-    let mut line_number = 1;
-    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
-    let mut lines_since_mismatch = context_size + 1;
-    let mut results = Vec::new();
-    let mut mismatch = Mismatch::new(0);
-
-    for result in diff::lines(expected, actual) {
-        match result {
-            diff::Result::Left(str) => {
-                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
-                    results.push(mismatch);
-                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
-                }
-
-                while let Some(line) = context_queue.pop_front() {
-                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
-                }
-
-                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
-                line_number += 1;
-                lines_since_mismatch = 0;
-            }
-            diff::Result::Right(str) => {
-                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
-                    results.push(mismatch);
-                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
-                }
-
-                while let Some(line) = context_queue.pop_front() {
-                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
-                }
-
-                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
-                lines_since_mismatch = 0;
-            }
-            diff::Result::Both(str, _) => {
-                if context_queue.len() >= context_size {
-                    let _ = context_queue.pop_front();
-                }
-
-                if lines_since_mismatch < context_size {
-                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
-                } else if context_size > 0 {
-                    context_queue.push_back(str);
-                }
-
-                line_number += 1;
-                lines_since_mismatch += 1;
-            }
-        }
-    }
-
-    results.push(mismatch);
-    results.remove(0);
-
-    results
-}
-
-fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
-    use std::fmt::Write;
-    let mut output = String::new();
-    let diff_results = make_diff(expected, actual, context_size);
-    for result in diff_results {
-        let mut line_number = result.line_number;
-        for line in result.lines {
-            match line {
-                DiffLine::Expected(e) => {
-                    writeln!(output, "-\t{}", e).unwrap();
-                    line_number += 1;
-                }
-                DiffLine::Context(c) => {
-                    writeln!(output, "{}\t{}", line_number, c).unwrap();
-                    line_number += 1;
-                }
-                DiffLine::Resulting(r) => {
-                    writeln!(output, "+\t{}", r).unwrap();
-                }
-            }
-        }
-        writeln!(output).unwrap();
-    }
-    output
-}
-
 pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
     match &*config.target {
         "arm-linux-androideabi"
@@ -2507,43 +2403,17 @@ impl<'test> TestCx<'test> {
 
         let diff_filename = format!("build/tmp/rustdoc-compare-{}.diff", std::process::id());
 
-        {
-            let mut diff_output = File::create(&diff_filename).unwrap();
-            let mut wrote_data = false;
-            for entry in walkdir::WalkDir::new(out_dir) {
-                let entry = entry.expect("failed to read file");
-                let extension = entry.path().extension().and_then(|p| p.to_str());
-                if entry.file_type().is_file()
+        if !write_filtered_diff(
+            &diff_filename,
+            out_dir,
+            &compare_dir,
+            self.config.verbose,
+            |file_type, extension| {
+                file_type.is_file()
                     && (extension == Some("html".into()) || extension == Some("js".into()))
-                {
-                    let expected_path =
-                        compare_dir.join(entry.path().strip_prefix(&out_dir).unwrap());
-                    let expected =
-                        if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
-                    let actual_path = entry.path();
-                    let actual = std::fs::read(&actual_path).unwrap();
-                    let diff = unified_diff::diff(
-                        &expected,
-                        &expected_path.to_string_lossy(),
-                        &actual,
-                        &actual_path.to_string_lossy(),
-                        3,
-                    );
-                    wrote_data |= !diff.is_empty();
-                    diff_output.write_all(&diff).unwrap();
-                }
-            }
-
-            if !wrote_data {
-                println!("note: diff is identical to nightly rustdoc");
-                assert!(diff_output.metadata().unwrap().len() == 0);
-                return;
-            } else if self.config.verbose {
-                eprintln!("printing diff:");
-                let mut buf = Vec::new();
-                diff_output.read_to_end(&mut buf).unwrap();
-                std::io::stderr().lock().write_all(&mut buf).unwrap();
-            }
+            },
+        ) {
+            return;
         }
 
         match self.config.color {