Rollup merge of #125409 - tbu-:pr_raw_dylib_only_windows, r=lcnr

Rename `FrameworkOnlyWindows` to `RawDylibOnlyWindows`

Frameworks are Apple-specific, no idea why it had "framework" in the name before.
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index fb66655..3d46415 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -661,11 +661,11 @@
             if attr_style == AttrStyle::Inner {
                 vec![
                     TokenTree::token_joint(token::Pound, span),
-                    TokenTree::token_alone(token::Not, span),
+                    TokenTree::token_joint_hidden(token::Not, span),
                     body,
                 ]
             } else {
-                vec![TokenTree::token_alone(token::Pound, span), body]
+                vec![TokenTree::token_joint_hidden(token::Pound, span), body]
             }
         }
     }
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index f6e9e1a..382c903 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -81,8 +81,17 @@
     }
 }
 
+pub enum TrailingBrace<'a> {
+    /// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
+    /// We will suggest changing the macro call to a different delimiter.
+    MacCall(&'a ast::MacCall),
+    /// Trailing brace in any other expression, such as `a + B {}`. We will
+    /// suggest wrapping the innermost expression in parentheses: `a + (B {})`.
+    Expr(&'a ast::Expr),
+}
+
 /// If an expression ends with `}`, returns the innermost expression ending in the `}`
-pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
+pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
     loop {
         match &expr.kind {
             AddrOf(_, _, e)
@@ -111,10 +120,14 @@
             | Struct(..)
             | TryBlock(..)
             | While(..)
-            | ConstBlock(_) => break Some(expr),
+            | ConstBlock(_) => break Some(TrailingBrace::Expr(expr)),
+
+            Cast(_, ty) => {
+                break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall);
+            }
 
             MacCall(mac) => {
-                break (mac.args.delim == Delimiter::Brace).then_some(expr);
+                break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
             }
 
             InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
@@ -131,7 +144,6 @@
             | MethodCall(_)
             | Tup(_)
             | Lit(_)
-            | Cast(_, _)
             | Type(_, _)
             | Await(_, _)
             | Field(_, _)
@@ -148,3 +160,78 @@
         }
     }
 }
+
+/// If the type's last token is `}`, it must be due to a braced macro call, such
+/// as in `*const brace! { ... }`. Returns that trailing macro call.
+fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
+    loop {
+        match &ty.kind {
+            ast::TyKind::MacCall(mac) => {
+                break (mac.args.delim == Delimiter::Brace).then_some(mac);
+            }
+
+            ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => {
+                ty = &mut_ty.ty;
+            }
+
+            ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output {
+                ast::FnRetTy::Default(_) => break None,
+                ast::FnRetTy::Ty(ret) => ty = ret,
+            },
+
+            ast::TyKind::Path(_, path) => match path_return_type(path) {
+                Some(trailing_ty) => ty = trailing_ty,
+                None => break None,
+            },
+
+            ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => {
+                match bounds.last() {
+                    Some(ast::GenericBound::Trait(bound, _)) => {
+                        match path_return_type(&bound.trait_ref.path) {
+                            Some(trailing_ty) => ty = trailing_ty,
+                            None => break None,
+                        }
+                    }
+                    Some(ast::GenericBound::Outlives(_)) | None => break None,
+                }
+            }
+
+            ast::TyKind::Slice(..)
+            | ast::TyKind::Array(..)
+            | ast::TyKind::Never
+            | ast::TyKind::Tup(..)
+            | ast::TyKind::Paren(..)
+            | ast::TyKind::Typeof(..)
+            | ast::TyKind::Infer
+            | ast::TyKind::ImplicitSelf
+            | ast::TyKind::CVarArgs
+            | ast::TyKind::Pat(..)
+            | ast::TyKind::Dummy
+            | ast::TyKind::Err(..) => break None,
+
+            // These end in brace, but cannot occur in a let-else statement.
+            // They are only parsed as fields of a data structure. For the
+            // purpose of denying trailing braces in the expression of a
+            // let-else, we can disregard these.
+            ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None,
+        }
+    }
+}
+
+/// Returns the trailing return type in the given path, if it has one.
+///
+/// ```ignore (illustrative)
+/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void
+///                             ^^^^^^^^^^^^^^^^^^^^^
+/// ```
+fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
+    let last_segment = path.segments.last()?;
+    let args = last_segment.args.as_ref()?;
+    match &**args {
+        ast::GenericArgs::Parenthesized(args) => match &args.output {
+            ast::FnRetTy::Default(_) => None,
+            ast::FnRetTy::Ty(ret) => Some(ret),
+        },
+        ast::GenericArgs::AngleBracketed(_) => None,
+    }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 545b98a..f02fe4c 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -681,22 +681,40 @@
         }
     }
 
+    // The easiest way to implement token stream pretty printing would be to
+    // print each token followed by a single space. But that would produce ugly
+    // output, so we go to some effort to do better.
+    //
+    // First, we track whether each token that appears in source code is
+    // followed by a space, with `Spacing`, and reproduce that in the output.
+    // This works well in a lot of cases. E.g. `stringify!(x + y)` produces
+    // "x + y" and `stringify!(x+y)` produces "x+y".
+    //
+    // But this doesn't work for code produced by proc macros (which have no
+    // original source text representation) nor for code produced by decl
+    // macros (which are tricky because the whitespace after tokens appearing
+    // in macro rules isn't always what you want in the produced output). For
+    // these we mostly use `Spacing::Alone`, which is the conservative choice.
+    //
+    // So we have a backup mechanism for when `Spacing::Alone` occurs between a
+    // pair of tokens: we check if that pair of tokens can obviously go
+    // together without a space between them. E.g. token `x` followed by token
+    // `,` is better printed as `x,` than `x ,`. (Even if the original source
+    // code was `x ,`.)
+    //
+    // Finally, we must be careful about changing the output. Token pretty
+    // printing is used by `stringify!` and `impl Display for
+    // proc_macro::TokenStream`, and some programs rely on the output having a
+    // particular form, even though they shouldn't. In particular, some proc
+    // macros do `format!({stream})` on a token stream and then "parse" the
+    // output with simple string matching that can't handle whitespace changes.
+    // E.g. we have seen cases where a proc macro can handle `a :: b` but not
+    // `a::b`. See #117433 for some examples.
     fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
         let mut iter = tts.trees().peekable();
         while let Some(tt) = iter.next() {
             let spacing = self.print_tt(tt, convert_dollar_crate);
             if let Some(next) = iter.peek() {
-                // Should we print a space after `tt`? There are two guiding
-                // factors.
-                // - `spacing` is the more important and accurate one. Most
-                //   tokens have good spacing information, and
-                //   `Joint`/`JointHidden` get used a lot.
-                // - `space_between` is the backup. Code produced by proc
-                //   macros has worse spacing information, with no
-                //   `JointHidden` usage and too much `Alone` usage, which
-                //   would result in over-spaced output such as
-                //   `( x () , y . z )`. `space_between` avoids some of the
-                //   excess whitespace.
                 if spacing == Spacing::Alone && space_between(tt, next) {
                     self.space();
                 }
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index 085ea34..7814422 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -153,7 +153,7 @@
     fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> {
         let escaped_expr_str = escape_to_fmt(expr_str);
         let initial = [
-            TokenTree::token_joint_hidden(
+            TokenTree::token_joint(
                 token::Literal(token::Lit {
                     kind: token::LitKind::Str,
                     symbol: Symbol::intern(&if self.fmt_string.is_empty() {
@@ -172,7 +172,7 @@
         ];
         let captures = self.capture_decls.iter().flat_map(|cap| {
             [
-                TokenTree::token_joint_hidden(
+                TokenTree::token_joint(
                     token::Ident(cap.ident.name, IdentIsRaw::No),
                     cap.ident.span,
                 ),
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index bf92ddb..577523a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -3,7 +3,7 @@
 use crate::errors;
 use core::ops::ControlFlow;
 use rustc_ast as ast;
-use rustc_ast::visit::walk_list;
+use rustc_ast::visit::visit_opt;
 use rustc_ast::{attr, EnumDef, VariantData};
 use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
 use rustc_span::symbol::Ident;
@@ -224,7 +224,7 @@
         self.visit_ident(v.ident);
         self.visit_vis(&v.vis);
         self.visit_variant_data(&v.data);
-        walk_list!(self, visit_anon_const, &v.disr_expr);
+        visit_opt!(self, visit_anon_const, &v.disr_expr);
         for attr in &v.attrs {
             rustc_ast::visit::walk_attribute(self, attr);
         }
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
index 0c7cffb..1625e6e 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
@@ -14,6 +14,7 @@
 use rustc_codegen_ssa::mir::place::PlaceRef;
 use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
 use rustc_hir as hir;
+use rustc_middle::mir::BinOp;
 use rustc_middle::span_bug;
 use rustc_middle::ty::layout::HasTyCtxt;
 use rustc_middle::ty::{self, Ty};
@@ -122,12 +123,12 @@
     let in_ty = arg_tys[0];
 
     let comparison = match name {
-        sym::simd_eq => Some(hir::BinOpKind::Eq),
-        sym::simd_ne => Some(hir::BinOpKind::Ne),
-        sym::simd_lt => Some(hir::BinOpKind::Lt),
-        sym::simd_le => Some(hir::BinOpKind::Le),
-        sym::simd_gt => Some(hir::BinOpKind::Gt),
-        sym::simd_ge => Some(hir::BinOpKind::Ge),
+        sym::simd_eq => Some(BinOp::Eq),
+        sym::simd_ne => Some(BinOp::Ne),
+        sym::simd_lt => Some(BinOp::Lt),
+        sym::simd_le => Some(BinOp::Le),
+        sym::simd_gt => Some(BinOp::Gt),
+        sym::simd_ge => Some(BinOp::Ge),
         _ => None,
     };
 
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 80e863a..897132a 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -14,6 +14,7 @@
 use rustc_codegen_ssa::mir::place::PlaceRef;
 use rustc_codegen_ssa::traits::*;
 use rustc_hir as hir;
+use rustc_middle::mir::BinOp;
 use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf};
 use rustc_middle::ty::{self, GenericArgsRef, Ty};
 use rustc_middle::{bug, span_bug};
@@ -1104,12 +1105,12 @@
     let in_ty = arg_tys[0];
 
     let comparison = match name {
-        sym::simd_eq => Some(hir::BinOpKind::Eq),
-        sym::simd_ne => Some(hir::BinOpKind::Ne),
-        sym::simd_lt => Some(hir::BinOpKind::Lt),
-        sym::simd_le => Some(hir::BinOpKind::Le),
-        sym::simd_gt => Some(hir::BinOpKind::Gt),
-        sym::simd_ge => Some(hir::BinOpKind::Ge),
+        sym::simd_eq => Some(BinOp::Eq),
+        sym::simd_ne => Some(BinOp::Ne),
+        sym::simd_lt => Some(BinOp::Lt),
+        sym::simd_le => Some(BinOp::Le),
+        sym::simd_gt => Some(BinOp::Gt),
+        sym::simd_ge => Some(BinOp::Ge),
         _ => None,
     };
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 37b8f81..c973bda 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -799,6 +799,27 @@
             continue;
         }
 
+        // Check if linking failed with an error message that indicates the driver didn't recognize
+        // the `-fuse-ld=lld` option. If so, re-perform the link step without it. This avoids having
+        // to spawn multiple instances on the happy path to do version checking, and ensures things
+        // keep working on the tier 1 baseline of GLIBC 2.17+. That is generally understood as GCCs
+        // circa RHEL/CentOS 7, 4.5 or so, whereas lld support was added in GCC 9.
+        if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::Yes))
+            && unknown_arg_regex.is_match(&out)
+            && out.contains("-fuse-ld=lld")
+            && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld")
+        {
+            info!("linker output: {:?}", out);
+            warn!("The linker driver does not support `-fuse-ld=lld`. Retrying without it.");
+            for arg in cmd.take_args() {
+                if arg.to_string_lossy() != "-fuse-ld=lld" {
+                    cmd.arg(arg);
+                }
+            }
+            info!("{:?}", &cmd);
+            continue;
+        }
+
         // Detect '-static-pie' used with an older version of gcc or clang not supporting it.
         // Fallback from '-static-pie' to '-static' in that case.
         if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 0e335bf..897ec2a 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -399,7 +399,7 @@
     tcx: TyCtxt<'_>,
     (): (),
 ) -> DefIdMap<UnordMap<GenericArgsRef<'_>, CrateNum>> {
-    let cnums = tcx.crates(());
+    let cnums = tcx.used_crates(());
 
     let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
 
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 877e5b7..67eb6c1 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -20,7 +20,6 @@
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
 use rustc_data_structures::sync::par_map;
 use rustc_data_structures::unord::UnordMap;
-use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::lang_items::LangItem;
 use rustc_metadata::EncodedMetadata;
@@ -30,6 +29,7 @@
 use rustc_middle::middle::exported_symbols;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_middle::middle::lang_items;
+use rustc_middle::mir::BinOp;
 use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
@@ -46,32 +46,32 @@
 
 use itertools::Itertools;
 
-pub fn bin_op_to_icmp_predicate(op: hir::BinOpKind, signed: bool) -> IntPredicate {
+pub fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
     match op {
-        hir::BinOpKind::Eq => IntPredicate::IntEQ,
-        hir::BinOpKind::Ne => IntPredicate::IntNE,
-        hir::BinOpKind::Lt => {
+        BinOp::Eq => IntPredicate::IntEQ,
+        BinOp::Ne => IntPredicate::IntNE,
+        BinOp::Lt => {
             if signed {
                 IntPredicate::IntSLT
             } else {
                 IntPredicate::IntULT
             }
         }
-        hir::BinOpKind::Le => {
+        BinOp::Le => {
             if signed {
                 IntPredicate::IntSLE
             } else {
                 IntPredicate::IntULE
             }
         }
-        hir::BinOpKind::Gt => {
+        BinOp::Gt => {
             if signed {
                 IntPredicate::IntSGT
             } else {
                 IntPredicate::IntUGT
             }
         }
-        hir::BinOpKind::Ge => {
+        BinOp::Ge => {
             if signed {
                 IntPredicate::IntSGE
             } else {
@@ -86,14 +86,14 @@
     }
 }
 
-pub fn bin_op_to_fcmp_predicate(op: hir::BinOpKind) -> RealPredicate {
+pub fn bin_op_to_fcmp_predicate(op: BinOp) -> RealPredicate {
     match op {
-        hir::BinOpKind::Eq => RealPredicate::RealOEQ,
-        hir::BinOpKind::Ne => RealPredicate::RealUNE,
-        hir::BinOpKind::Lt => RealPredicate::RealOLT,
-        hir::BinOpKind::Le => RealPredicate::RealOLE,
-        hir::BinOpKind::Gt => RealPredicate::RealOGT,
-        hir::BinOpKind::Ge => RealPredicate::RealOGE,
+        BinOp::Eq => RealPredicate::RealOEQ,
+        BinOp::Ne => RealPredicate::RealUNE,
+        BinOp::Lt => RealPredicate::RealOLT,
+        BinOp::Le => RealPredicate::RealOLE,
+        BinOp::Gt => RealPredicate::RealOGT,
+        BinOp::Ge => RealPredicate::RealOGE,
         op => {
             bug!(
                 "comparison_op_to_fcmp_predicate: expected comparison operator, \
@@ -110,7 +110,7 @@
     rhs: Bx::Value,
     t: Ty<'tcx>,
     ret_ty: Bx::Type,
-    op: hir::BinOpKind,
+    op: BinOp,
 ) -> Bx::Value {
     let signed = match t.kind() {
         ty::Float(_) => {
@@ -539,7 +539,7 @@
     tcx.debugger_visualizers(LOCAL_CRATE)
         .iter()
         .chain(
-            tcx.crates(())
+            tcx.used_crates(())
                 .iter()
                 .filter(|&cnum| {
                     let used_crate_source = tcx.used_crate_source(*cnum);
@@ -849,7 +849,7 @@
         // `compiler_builtins` are always placed last to ensure that they're linked correctly.
         used_crates.extend(compiler_builtins);
 
-        let crates = tcx.crates(());
+        let crates = tcx.used_crates(());
         let n_crates = crates.len();
         let mut info = CrateInfo {
             target_cpu,
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 4eb24d7..66a7a2e 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -195,6 +195,7 @@
     EmptyVersionNumber,
     EncodingVersionMismatch { version_array: String, rlink_version: u32 },
     RustcVersionMismatch { rustc_version: String },
+    CorruptFile,
 }
 
 pub fn provide(providers: &mut Providers) {
@@ -265,7 +266,9 @@
             });
         }
 
-        let mut decoder = MemDecoder::new(&data[4..], 0);
+        let Ok(mut decoder) = MemDecoder::new(&data[4..], 0) else {
+            return Err(CodegenErrors::CorruptFile);
+        };
         let rustc_version = decoder.read_str();
         if rustc_version != sess.cfg_version {
             return Err(CodegenErrors::RustcVersionMismatch {
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 00b28cb..f9085f5 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -7,7 +7,6 @@
 use crate::traits::*;
 use crate::MemFlags;
 
-use rustc_hir as hir;
 use rustc_middle::mir;
 use rustc_middle::ty::cast::{CastTy, IntTy};
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
@@ -896,9 +895,9 @@
             | mir::BinOp::Le
             | mir::BinOp::Ge => {
                 if is_float {
-                    bx.fcmp(base::bin_op_to_fcmp_predicate(op.to_hir_binop()), lhs, rhs)
+                    bx.fcmp(base::bin_op_to_fcmp_predicate(op), lhs, rhs)
                 } else {
-                    bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs)
+                    bx.icmp(base::bin_op_to_icmp_predicate(op, is_signed), lhs, rhs)
                 }
             }
             mir::BinOp::Cmp => {
@@ -912,16 +911,16 @@
                     // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it
                     // better (see <https://github.com/llvm/llvm-project/issues/73417>), it'll
                     // be worth trying it in optimized builds as well.
-                    let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs);
+                    let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs);
                     let gtext = bx.zext(is_gt, bx.type_i8());
-                    let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs);
+                    let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs);
                     let ltext = bx.zext(is_lt, bx.type_i8());
                     bx.unchecked_ssub(gtext, ltext)
                 } else {
                     // These operations are those expected by `tests/codegen/integer-cmp.rs`,
                     // from <https://github.com/rust-lang/rust/pull/63767>.
-                    let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs);
-                    let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs);
+                    let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs);
+                    let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs);
                     let ge = bx.select(
                         is_ne,
                         bx.cx().const_i8(Ordering::Greater as i8),
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 20f0f27..2dbeb7d 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -246,11 +246,10 @@
 
 const_eval_operator_non_const =
     cannot call non-const operator in {const_eval_const_context}s
-const_eval_overflow =
-    overflow executing `{$name}`
-
+const_eval_overflow_arith =
+    arithmetic overflow in `{$intrinsic}`
 const_eval_overflow_shift =
-    overflowing shift by {$val} in `{$name}`
+    overflowing shift by {$shift_amount} in `{$intrinsic}`
 
 const_eval_panic =
     the evaluated program panicked at '{$msg}', {$file}:{$line}:{$col}
diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs
index 94c9f05..530a05a 100644
--- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs
@@ -125,7 +125,7 @@
         bin_op: BinOp,
         left: &interpret::ImmTy<'tcx, Self::Provenance>,
         right: &interpret::ImmTy<'tcx, Self::Provenance>,
-    ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> {
+    ) -> interpret::InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> {
         use rustc_middle::mir::BinOp::*;
         Ok(match bin_op {
             Eq | Ne | Lt | Le | Gt | Ge => {
@@ -154,7 +154,7 @@
                     Ge => left >= right,
                     _ => bug!(),
                 };
-                (ImmTy::from_bool(res, *ecx.tcx), false)
+                ImmTy::from_bool(res, *ecx.tcx)
             }
 
             // Some more operations are possible with atomics.
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 08c9609..7a1c2a7b 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -2,7 +2,7 @@
 
 use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg};
 use rustc_hir::CRATE_HIR_ID;
-use rustc_middle::mir::interpret::Provenance;
+use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo};
 use rustc_middle::mir::AssertKind;
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::TyCtxt;
@@ -139,9 +139,10 @@
             ErrorHandled::TooGeneric(span)
         }
         err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar, span),
-        err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
-            ErrorHandled::Reported(guar.into(), span)
-        }
+        err_inval!(Layout(LayoutError::ReferencesError(guar))) => ErrorHandled::Reported(
+            ReportedErrorInfo::tainted_by_errors(guar),
+            span,
+        ),
         // Report remaining errors.
         _ => {
             let (our_span, frames) = get_span_and_frames();
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 836e548..3cd6027 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -25,9 +25,9 @@
 use crate::errors::{LongRunning, LongRunningWarn};
 use crate::fluent_generated as fluent;
 use crate::interpret::{
-    self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom,
+    self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup,
     throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame,
-    ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
+    GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
 };
 
 use super::error::*;
@@ -589,7 +589,7 @@
         _bin_op: mir::BinOp,
         _left: &ImmTy<'tcx>,
         _right: &ImmTy<'tcx>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx>, bool)> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
         throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time");
     }
 
@@ -759,11 +759,21 @@
         ecx: &InterpCx<'mir, 'tcx, Self>,
         alloc_id: AllocId,
     ) -> InterpResult<'tcx> {
+        // Check if this is the currently evaluated static.
         if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
-            Err(ConstEvalErrKind::RecursiveStatic.into())
-        } else {
-            Ok(())
+            return Err(ConstEvalErrKind::RecursiveStatic.into());
         }
+        // If this is another static, make sure we fire off the query to detect cycles.
+        // But only do that when checks for static recursion are enabled.
+        if ecx.machine.static_root_ids.is_some() {
+            if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) {
+                if ecx.tcx.is_foreign_item(def_id) {
+                    throw_unsup!(ExternStatic(def_id));
+                }
+                ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
+            }
+        }
+        Ok(())
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 90d4f11..e5ea4c3 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -1,5 +1,6 @@
 use std::borrow::Cow;
 
+use either::Either;
 use rustc_errors::{
     codes::*, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic, EmissionGuarantee, Level,
 };
@@ -481,6 +482,8 @@
             DivisionOverflow => const_eval_division_overflow,
             RemainderOverflow => const_eval_remainder_overflow,
             PointerArithOverflow => const_eval_pointer_arithmetic_overflow,
+            ArithOverflow { .. } => const_eval_overflow_arith,
+            ShiftOverflow { .. } => const_eval_overflow_shift,
             InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice,
             InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta,
             UnterminatedCString(_) => const_eval_unterminated_c_string,
@@ -539,6 +542,19 @@
             | UninhabitedEnumVariantWritten(_)
             | UninhabitedEnumVariantRead(_) => {}
 
+            ArithOverflow { intrinsic } => {
+                diag.arg("intrinsic", intrinsic);
+            }
+            ShiftOverflow { intrinsic, shift_amount } => {
+                diag.arg("intrinsic", intrinsic);
+                diag.arg(
+                    "shift_amount",
+                    match shift_amount {
+                        Either::Left(v) => v.to_string(),
+                        Either::Right(v) => v.to_string(),
+                    },
+                );
+            }
             BoundsCheckFailed { len, index } => {
                 diag.arg("len", len);
                 diag.arg("index", index);
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index 8ddc741..f0f898f 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -172,7 +172,7 @@
                         let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
                         let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
                         let variant_index_relative_val =
-                            self.wrapping_binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
+                            self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
                         let variant_index_relative =
                             variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
                         // Check if this is in the range that indicates an actual discriminant.
@@ -292,11 +292,7 @@
                 let variant_index_relative_val =
                     ImmTy::from_uint(variant_index_relative, tag_layout);
                 let tag = self
-                    .wrapping_binary_op(
-                        mir::BinOp::Add,
-                        &variant_index_relative_val,
-                        &niche_start_val,
-                    )?
+                    .binary_op(mir::BinOp::Add, &variant_index_relative_val, &niche_start_val)?
                     .to_scalar()
                     .assert_int();
                 Ok(Some((tag, tag_field)))
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 344bb7c..1207150 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -1181,9 +1181,20 @@
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
             let const_val = val.eval(*ecx.tcx, ecx.param_env, span).map_err(|err| {
-                if M::ALL_CONSTS_ARE_PRECHECKED && !matches!(err, ErrorHandled::TooGeneric(..)) {
-                    // Looks like the const is not captued by `required_consts`, that's bad.
-                    bug!("interpret const eval failure of {val:?} which is not in required_consts");
+                if M::ALL_CONSTS_ARE_PRECHECKED {
+                    match err {
+                        ErrorHandled::TooGeneric(..) => {},
+                        ErrorHandled::Reported(reported, span) => {
+                            if reported.is_tainted_by_errors() {
+                                // const-eval will return "tainted" errors if e.g. the layout cannot
+                                // be computed as the type references non-existing names.
+                                // See <https://github.com/rust-lang/rust/issues/124348>.
+                            } else {
+                                // Looks like the const is not captued by `required_consts`, that's bad.
+                                span_bug!(span, "interpret const eval failure of {val:?} which is not in required_consts");
+                            }
+                        }
+                    }
                 }
                 err.emit_note(*ecx.tcx);
                 err
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index dce4d56..e7a46ba 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -255,6 +255,7 @@
                                     name = intrinsic_name,
                                 );
                             }
+                            // This will always return 0.
                             (a, b)
                         }
                         (Err(_), _) | (_, Err(_)) => {
@@ -285,9 +286,10 @@
                     let (val, overflowed) = {
                         let a_offset = ImmTy::from_uint(a_offset, usize_layout);
                         let b_offset = ImmTy::from_uint(b_offset, usize_layout);
-                        self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?
+                        self.binary_op(BinOp::SubWithOverflow, &a_offset, &b_offset)?
+                            .to_scalar_pair()
                     };
-                    if overflowed {
+                    if overflowed.to_bool()? {
                         // a < b
                         if intrinsic_name == sym::ptr_offset_from_unsigned {
                             throw_ub_custom!(
@@ -299,7 +301,7 @@
                         // The signed form of the intrinsic allows this. If we interpret the
                         // difference as isize, we'll get the proper signed difference. If that
                         // seems *positive*, they were more than isize::MAX apart.
-                        let dist = val.to_scalar().to_target_isize(self)?;
+                        let dist = val.to_target_isize(self)?;
                         if dist >= 0 {
                             throw_ub_custom!(
                                 fluent::const_eval_offset_from_underflow,
@@ -309,7 +311,7 @@
                         dist
                     } else {
                         // b >= a
-                        let dist = val.to_scalar().to_target_isize(self)?;
+                        let dist = val.to_target_isize(self)?;
                         // If converting to isize produced a *negative* result, we had an overflow
                         // because they were more than isize::MAX apart.
                         if dist < 0 {
@@ -515,9 +517,8 @@
         // Performs an exact division, resulting in undefined behavior where
         // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
         // First, check x % y != 0 (or if that computation overflows).
-        let (res, overflow) = self.overflowing_binary_op(BinOp::Rem, a, b)?;
-        assert!(!overflow); // All overflow is UB, so this should never return on overflow.
-        if res.to_scalar().assert_bits(a.layout.size) != 0 {
+        let rem = self.binary_op(BinOp::Rem, a, b)?;
+        if rem.to_scalar().assert_bits(a.layout.size) != 0 {
             throw_ub_custom!(
                 fluent::const_eval_exact_div_has_remainder,
                 a = format!("{a}"),
@@ -525,7 +526,8 @@
             )
         }
         // `Rem` says this is all right, so we can let `Div` do its job.
-        self.binop_ignore_overflow(BinOp::Div, a, b, &dest.clone().into())
+        let res = self.binary_op(BinOp::Div, a, b)?;
+        self.write_immediate(*res, dest)
     }
 
     pub fn saturating_arith(
@@ -538,8 +540,9 @@
         assert!(matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..)));
         assert!(matches!(mir_op, BinOp::Add | BinOp::Sub));
 
-        let (val, overflowed) = self.overflowing_binary_op(mir_op, l, r)?;
-        Ok(if overflowed {
+        let (val, overflowed) =
+            self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair();
+        Ok(if overflowed.to_bool()? {
             let size = l.layout.size;
             let num_bits = size.bits();
             if l.layout.abi.is_signed() {
@@ -570,7 +573,7 @@
                 }
             }
         } else {
-            val.to_scalar()
+            val
         })
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 2eaebc1..72a16db 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -252,7 +252,7 @@
         bin_op: mir::BinOp,
         left: &ImmTy<'tcx, Self::Provenance>,
         right: &ImmTy<'tcx, Self::Provenance>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)>;
+    ) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>>;
 
     /// Generate the NaN returned by a float operation, given the list of inputs.
     /// (This is all inputs, not just NaN inputs!)
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 350fd48..737f2fd 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -413,6 +413,8 @@
     /// to the allocation it points to. Supports both shared and mutable references, as the actual
     /// checking is offloaded to a helper closure.
     ///
+    /// `alloc_size` will only get called for non-zero-sized accesses.
+    ///
     /// Returns `None` if and only if the size is 0.
     fn check_and_deref_ptr<T>(
         &self,
@@ -425,18 +427,19 @@
             M::ProvenanceExtra,
         ) -> InterpResult<'tcx, (Size, Align, T)>,
     ) -> InterpResult<'tcx, Option<T>> {
+        // Everything is okay with size 0.
+        if size.bytes() == 0 {
+            return Ok(None);
+        }
+
         Ok(match self.ptr_try_get_alloc_id(ptr) {
             Err(addr) => {
-                // We couldn't get a proper allocation. This is only okay if the access size is 0,
-                // and the address is not null.
-                if size.bytes() > 0 || addr == 0 {
-                    throw_ub!(DanglingIntPointer(addr, msg));
-                }
-                None
+                // We couldn't get a proper allocation.
+                throw_ub!(DanglingIntPointer(addr, msg));
             }
             Ok((alloc_id, offset, prov)) => {
                 let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
-                // Test bounds. This also ensures non-null.
+                // Test bounds.
                 // It is sufficient to check this for the end pointer. Also check for overflow!
                 if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
                     throw_ub!(PointerOutOfBounds {
@@ -447,14 +450,8 @@
                         msg,
                     })
                 }
-                // Ensure we never consider the null pointer dereferenceable.
-                if M::Provenance::OFFSET_IS_ADDR {
-                    assert_ne!(ptr.addr(), Size::ZERO);
-                }
 
-                // We can still be zero-sized in this branch, in which case we have to
-                // return `None`.
-                if size.bytes() == 0 { None } else { Some(ret_val) }
+                Some(ret_val)
             }
         })
     }
@@ -641,16 +638,18 @@
             size,
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, prov| {
-                if !self.memory.validation_in_progress.get() {
-                    // We want to call the hook on *all* accesses that involve an AllocId,
-                    // including zero-sized accesses. That means we have to do it here
-                    // rather than below in the `Some` branch.
-                    M::before_alloc_read(self, alloc_id)?;
-                }
                 let alloc = self.get_alloc_raw(alloc_id)?;
                 Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
             },
         )?;
+        // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
+        // accesses. That means we cannot rely on the closure above or the `Some` branch below. We
+        // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
+        if !self.memory.validation_in_progress.get() {
+            if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) {
+                M::before_alloc_read(self, alloc_id)?;
+            }
+        }
 
         if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index bad9732..71d22e1 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -7,7 +7,7 @@
 
 use rustc_hir::def::Namespace;
 use rustc_middle::mir::interpret::ScalarSizeMismatch;
-use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
 use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
@@ -249,6 +249,15 @@
         Self::from_scalar(Scalar::from_i8(c as i8), layout)
     }
 
+    pub fn from_pair(a: Self, b: Self, tcx: TyCtxt<'tcx>) -> Self {
+        let layout = tcx
+            .layout_of(
+                ty::ParamEnv::reveal_all().and(Ty::new_tup(tcx, &[a.layout.ty, b.layout.ty])),
+            )
+            .unwrap();
+        Self::from_scalar_pair(a.to_scalar(), b.to_scalar(), layout)
+    }
+
     /// Return the immediate as a `ScalarInt`. Ensures that it has the size that the layout of the
     /// immediate indicates.
     #[inline]
@@ -270,6 +279,17 @@
         ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral())
     }
 
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
+    pub fn to_pair(self, cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>)) -> (Self, Self) {
+        let layout = self.layout;
+        let (val0, val1) = self.to_scalar_pair();
+        (
+            ImmTy::from_scalar(val0, layout.field(cx, 0)),
+            ImmTy::from_scalar(val1, layout.field(cx, 1)),
+        )
+    }
+
     /// Compute the "sub-immediate" that is located within the `base` at the given offset with the
     /// given layout.
     // Not called `offset` to avoid confusion with the trait method.
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 5f59e3d..6992856 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -1,78 +1,22 @@
+use either::Either;
+
 use rustc_apfloat::{Float, FloatConvert};
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
-use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty};
+use rustc_middle::ty::{self, FloatTy, ScalarInt};
 use rustc_middle::{bug, span_bug};
 use rustc_span::symbol::sym;
-use rustc_target::abi::Abi;
 
-use super::{err_ub, throw_ub, throw_ub_custom, ImmTy, Immediate, InterpCx, Machine, PlaceTy};
-
-use crate::fluent_generated as fluent;
+use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine};
 
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
-    /// Applies the binary operation `op` to the two operands and writes a tuple of the result
-    /// and a boolean signifying the potential overflow to the destination.
-    pub fn binop_with_overflow(
-        &mut self,
-        op: mir::BinOp,
-        left: &ImmTy<'tcx, M::Provenance>,
-        right: &ImmTy<'tcx, M::Provenance>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx> {
-        let (val, overflowed) = self.overflowing_binary_op(op, left, right)?;
-        debug_assert_eq!(
-            Ty::new_tup(self.tcx.tcx, &[val.layout.ty, self.tcx.types.bool]),
-            dest.layout.ty,
-            "type mismatch for result of {op:?}",
-        );
-        // Write the result to `dest`.
-        if let Abi::ScalarPair(..) = dest.layout.abi {
-            // We can use the optimized path and avoid `place_field` (which might do
-            // `force_allocation`).
-            let pair = Immediate::ScalarPair(val.to_scalar(), Scalar::from_bool(overflowed));
-            self.write_immediate(pair, dest)?;
-        } else {
-            assert!(self.tcx.sess.opts.unstable_opts.randomize_layout);
-            // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to
-            // do a component-wise write here. This code path is slower than the above because
-            // `place_field` will have to `force_allocate` locals here.
-            let val_field = self.project_field(dest, 0)?;
-            self.write_scalar(val.to_scalar(), &val_field)?;
-            let overflowed_field = self.project_field(dest, 1)?;
-            self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?;
-        }
-        Ok(())
-    }
-
-    /// Applies the binary operation `op` to the arguments and writes the result to the
-    /// destination.
-    pub fn binop_ignore_overflow(
-        &mut self,
-        op: mir::BinOp,
-        left: &ImmTy<'tcx, M::Provenance>,
-        right: &ImmTy<'tcx, M::Provenance>,
-        dest: &PlaceTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx> {
-        let val = self.wrapping_binary_op(op, left, right)?;
-        assert_eq!(val.layout.ty, dest.layout.ty, "type mismatch for result of {op:?}");
-        self.write_immediate(*val, dest)
-    }
-}
-
-impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
-    fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> (ImmTy<'tcx, M::Provenance>, bool) {
+    fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
         let res = Ord::cmp(&lhs, &rhs);
-        return (ImmTy::from_ordering(res, *self.tcx), false);
+        return ImmTy::from_ordering(res, *self.tcx);
     }
 
-    fn binary_char_op(
-        &self,
-        bin_op: mir::BinOp,
-        l: char,
-        r: char,
-    ) -> (ImmTy<'tcx, M::Provenance>, bool) {
+    fn binary_char_op(&self, bin_op: mir::BinOp, l: char, r: char) -> ImmTy<'tcx, M::Provenance> {
         use rustc_middle::mir::BinOp::*;
 
         if bin_op == Cmp {
@@ -88,15 +32,10 @@
             Ge => l >= r,
             _ => span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op),
         };
-        (ImmTy::from_bool(res, *self.tcx), false)
+        ImmTy::from_bool(res, *self.tcx)
     }
 
-    fn binary_bool_op(
-        &self,
-        bin_op: mir::BinOp,
-        l: bool,
-        r: bool,
-    ) -> (ImmTy<'tcx, M::Provenance>, bool) {
+    fn binary_bool_op(&self, bin_op: mir::BinOp, l: bool, r: bool) -> ImmTy<'tcx, M::Provenance> {
         use rustc_middle::mir::BinOp::*;
 
         let res = match bin_op {
@@ -111,7 +50,7 @@
             BitXor => l ^ r,
             _ => span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op),
         };
-        (ImmTy::from_bool(res, *self.tcx), false)
+        ImmTy::from_bool(res, *self.tcx)
     }
 
     fn binary_float_op<F: Float + FloatConvert<F> + Into<Scalar<M::Provenance>>>(
@@ -120,14 +59,14 @@
         layout: TyAndLayout<'tcx>,
         l: F,
         r: F,
-    ) -> (ImmTy<'tcx, M::Provenance>, bool) {
+    ) -> ImmTy<'tcx, M::Provenance> {
         use rustc_middle::mir::BinOp::*;
 
         // Performs appropriate non-deterministic adjustments of NaN results.
         let adjust_nan =
             |f: F| -> F { if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f } };
 
-        let val = match bin_op {
+        match bin_op {
             Eq => ImmTy::from_bool(l == r, *self.tcx),
             Ne => ImmTy::from_bool(l != r, *self.tcx),
             Lt => ImmTy::from_bool(l < r, *self.tcx),
@@ -140,8 +79,7 @@
             Div => ImmTy::from_scalar(adjust_nan((l / r).value).into(), layout),
             Rem => ImmTy::from_scalar(adjust_nan((l % r).value).into(), layout),
             _ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op),
-        };
-        (val, false)
+        }
     }
 
     fn binary_int_op(
@@ -149,7 +87,7 @@
         bin_op: mir::BinOp,
         left: &ImmTy<'tcx, M::Provenance>,
         right: &ImmTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         use rustc_middle::mir::BinOp::*;
 
         // This checks the size, so that we can just assert it below.
@@ -169,25 +107,27 @@
             ShrUnchecked => Some(sym::unchecked_shr),
             _ => None,
         };
+        let with_overflow = bin_op.is_overflowing();
 
         // Shift ops can have an RHS with a different numeric type.
         if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
             let size = left.layout.size.bits();
-            // The shift offset is implicitly masked to the type size. (This is the one MIR operator
-            // that does *not* directly map to a single LLVM operation.) Compute how much we
-            // actually shift and whether there was an overflow due to shifting too much.
+            // Compute the equivalent shift modulo `size` that is in the range `0..size`. (This is
+            // the one MIR operator that does *not* directly map to a single LLVM operation.)
             let (shift_amount, overflow) = if right.layout.abi.is_signed() {
                 let shift_amount = r_signed();
                 let overflow = shift_amount < 0 || shift_amount >= i128::from(size);
                 // Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result
                 // of the `as` will be equal modulo `size` (since it is a power of two).
                 let masked_amount = (shift_amount as u128) % u128::from(size);
-                assert_eq!(overflow, shift_amount != (masked_amount as i128));
+                assert_eq!(overflow, shift_amount != i128::try_from(masked_amount).unwrap());
                 (masked_amount, overflow)
             } else {
                 let shift_amount = r_unsigned();
+                let overflow = shift_amount >= u128::from(size);
                 let masked_amount = shift_amount % u128::from(size);
-                (masked_amount, shift_amount != masked_amount)
+                assert_eq!(overflow, shift_amount != masked_amount);
+                (masked_amount, overflow)
             };
             let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit
             // Compute the shifted result.
@@ -209,19 +149,18 @@
                 ScalarInt::truncate_from_uint(result, left.layout.size).0
             };
 
-            if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
-                throw_ub_custom!(
-                    fluent::const_eval_overflow_shift,
-                    val = if right.layout.abi.is_signed() {
-                        r_signed().to_string()
+            if overflow && let Some(intrinsic) = throw_ub_on_overflow {
+                throw_ub!(ShiftOverflow {
+                    intrinsic,
+                    shift_amount: if right.layout.abi.is_signed() {
+                        Either::Right(r_signed())
                     } else {
-                        r_unsigned().to_string()
-                    },
-                    name = intrinsic_name
-                );
+                        Either::Left(r_unsigned())
+                    }
+                });
             }
 
-            return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
+            return Ok(ImmTy::from_scalar_int(result, left.layout));
         }
 
         // For the remaining ops, the types must be the same on both sides
@@ -246,7 +185,7 @@
                 _ => None,
             };
             if let Some(op) = op {
-                return Ok((ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx), false));
+                return Ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx));
             }
             if bin_op == Cmp {
                 return Ok(self.three_way_compare(l_signed(), r_signed()));
@@ -256,9 +195,9 @@
                 Rem if r.is_null() => throw_ub!(RemainderByZero),
                 Div => Some(i128::overflowing_div),
                 Rem => Some(i128::overflowing_rem),
-                Add | AddUnchecked => Some(i128::overflowing_add),
-                Sub | SubUnchecked => Some(i128::overflowing_sub),
-                Mul | MulUnchecked => Some(i128::overflowing_mul),
+                Add | AddUnchecked | AddWithOverflow => Some(i128::overflowing_add),
+                Sub | SubUnchecked | SubWithOverflow => Some(i128::overflowing_sub),
+                Mul | MulUnchecked | MulWithOverflow => Some(i128::overflowing_mul),
                 _ => None,
             };
             if let Some(op) = op {
@@ -282,10 +221,16 @@
                 // If that truncation loses any information, we have an overflow.
                 let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size);
                 let overflow = oflo || lossy;
-                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
-                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                if overflow && let Some(intrinsic) = throw_ub_on_overflow {
+                    throw_ub!(ArithOverflow { intrinsic });
                 }
-                return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
+                let res = ImmTy::from_scalar_int(result, left.layout);
+                return Ok(if with_overflow {
+                    let overflow = ImmTy::from_bool(overflow, *self.tcx);
+                    ImmTy::from_pair(res, overflow, *self.tcx)
+                } else {
+                    res
+                });
             }
         }
         // From here on it's okay to treat everything as unsigned.
@@ -296,7 +241,7 @@
             return Ok(self.three_way_compare(l, r));
         }
 
-        let val = match bin_op {
+        Ok(match bin_op {
             Eq => ImmTy::from_bool(l == r, *self.tcx),
             Ne => ImmTy::from_bool(l != r, *self.tcx),
 
@@ -309,40 +254,42 @@
             BitAnd => ImmTy::from_uint(l & r, left.layout),
             BitXor => ImmTy::from_uint(l ^ r, left.layout),
 
-            Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
+            _ => {
                 assert!(!left.layout.abi.is_signed());
                 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
-                    Add | AddUnchecked => u128::overflowing_add,
-                    Sub | SubUnchecked => u128::overflowing_sub,
-                    Mul | MulUnchecked => u128::overflowing_mul,
+                    Add | AddUnchecked | AddWithOverflow => u128::overflowing_add,
+                    Sub | SubUnchecked | SubWithOverflow => u128::overflowing_sub,
+                    Mul | MulUnchecked | MulWithOverflow => u128::overflowing_mul,
                     Div if r == 0 => throw_ub!(DivisionByZero),
                     Rem if r == 0 => throw_ub!(RemainderByZero),
                     Div => u128::overflowing_div,
                     Rem => u128::overflowing_rem,
-                    _ => bug!(),
+                    _ => span_bug!(
+                        self.cur_span(),
+                        "invalid binary op {:?}: {:?}, {:?} (both {})",
+                        bin_op,
+                        left,
+                        right,
+                        right.layout.ty,
+                    ),
                 };
                 let (result, oflo) = op(l, r);
                 // Truncate to target type.
                 // If that truncation loses any information, we have an overflow.
                 let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size);
                 let overflow = oflo || lossy;
-                if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
-                    throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+                if overflow && let Some(intrinsic) = throw_ub_on_overflow {
+                    throw_ub!(ArithOverflow { intrinsic });
                 }
-                return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
+                let res = ImmTy::from_scalar_int(result, left.layout);
+                if with_overflow {
+                    let overflow = ImmTy::from_bool(overflow, *self.tcx);
+                    ImmTy::from_pair(res, overflow, *self.tcx)
+                } else {
+                    res
+                }
             }
-
-            _ => span_bug!(
-                self.cur_span(),
-                "invalid binary op {:?}: {:?}, {:?} (both {})",
-                bin_op,
-                left,
-                right,
-                right.layout.ty,
-            ),
-        };
-
-        Ok((val, false))
+        })
     }
 
     fn binary_ptr_op(
@@ -350,7 +297,7 @@
         bin_op: mir::BinOp,
         left: &ImmTy<'tcx, M::Provenance>,
         right: &ImmTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         use rustc_middle::mir::BinOp::*;
 
         match bin_op {
@@ -369,10 +316,7 @@
                     offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
 
                 let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
-                Ok((
-                    ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout),
-                    false,
-                ))
+                Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout))
             }
 
             // Fall back to machine hook so Miri can support more pointer ops.
@@ -380,13 +324,15 @@
         }
     }
 
-    /// Returns the result of the specified operation, and whether it overflowed.
-    pub fn overflowing_binary_op(
+    /// Returns the result of the specified operation.
+    ///
+    /// Whether this produces a scalar or a pair depends on the specific `bin_op`.
+    pub fn binary_op(
         &self,
         bin_op: mir::BinOp,
         left: &ImmTy<'tcx, M::Provenance>,
         right: &ImmTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         trace!(
             "Running binary op {:?}: {:?} ({}), {:?} ({})",
             bin_op,
@@ -458,24 +404,13 @@
         }
     }
 
-    #[inline]
-    pub fn wrapping_binary_op(
-        &self,
-        bin_op: mir::BinOp,
-        left: &ImmTy<'tcx, M::Provenance>,
-        right: &ImmTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
-        let (val, _overflow) = self.overflowing_binary_op(bin_op, left, right)?;
-        Ok(val)
-    }
-
     /// Returns the result of the specified operation, whether it overflowed, and
     /// the result type.
-    pub fn overflowing_unary_op(
+    pub fn unary_op(
         &self,
         un_op: mir::UnOp,
         val: &ImmTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         use rustc_middle::mir::UnOp::*;
 
         let layout = val.layout;
@@ -489,7 +424,7 @@
                     Not => !val,
                     _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op),
                 };
-                Ok((ImmTy::from_bool(res, *self.tcx), false))
+                Ok(ImmTy::from_bool(res, *self.tcx))
             }
             ty::Float(fty) => {
                 // No NaN adjustment here, `-` is a bitwise operation!
@@ -498,37 +433,25 @@
                     (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
                     _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op),
                 };
-                Ok((ImmTy::from_scalar(res, layout), false))
+                Ok(ImmTy::from_scalar(res, layout))
             }
             _ => {
                 assert!(layout.ty.is_integral());
                 let val = val.to_bits(layout.size)?;
-                let (res, overflow) = match un_op {
-                    Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate
+                let res = match un_op {
+                    Not => self.truncate(!val, layout), // bitwise negation, then truncate
                     Neg => {
                         // arithmetic negation
                         assert!(layout.abi.is_signed());
                         let val = self.sign_extend(val, layout) as i128;
-                        let (res, overflow) = val.overflowing_neg();
+                        let res = val.wrapping_neg();
                         let res = res as u128;
                         // Truncate to target type.
-                        // If that truncation loses any information, we have an overflow.
-                        let truncated = self.truncate(res, layout);
-                        (truncated, overflow || self.sign_extend(truncated, layout) != res)
+                        self.truncate(res, layout)
                     }
                 };
-                Ok((ImmTy::from_uint(res, layout), overflow))
+                Ok(ImmTy::from_uint(res, layout))
             }
         }
     }
-
-    #[inline]
-    pub fn wrapping_unary_op(
-        &self,
-        un_op: mir::UnOp,
-        val: &ImmTy<'tcx, M::Provenance>,
-    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
-        let (val, _overflow) = self.overflowing_unary_op(un_op, val)?;
-        Ok(val)
-    }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index cb72d55..4d653bc 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -167,19 +167,17 @@
                 let left = self.read_immediate(&self.eval_operand(left, layout)?)?;
                 let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
                 let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
-                if let Some(bin_op) = bin_op.overflowing_to_wrapping() {
-                    self.binop_with_overflow(bin_op, &left, &right, &dest)?;
-                } else {
-                    self.binop_ignore_overflow(bin_op, &left, &right, &dest)?;
-                }
+                let result = self.binary_op(bin_op, &left, &right)?;
+                assert_eq!(result.layout, dest.layout, "layout mismatch for result of {bin_op:?}");
+                self.write_immediate(*result, &dest)?;
             }
 
             UnaryOp(un_op, ref operand) => {
                 // The operand always has the same type as the result.
                 let val = self.read_immediate(&self.eval_operand(operand, Some(dest.layout))?)?;
-                let val = self.wrapping_unary_op(un_op, &val)?;
-                assert_eq!(val.layout, dest.layout, "layout mismatch for result of {un_op:?}");
-                self.write_immediate(*val, &dest)?;
+                let result = self.unary_op(un_op, &val)?;
+                assert_eq!(result.layout, dest.layout, "layout mismatch for result of {un_op:?}");
+                self.write_immediate(*result, &dest)?;
             }
 
             Aggregate(box ref kind, ref operands) => {
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index b82c185..cfe4a3d9 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -97,7 +97,7 @@
                 for (const_int, target) in targets.iter() {
                     // Compare using MIR BinOp::Eq, to also support pointer values.
                     // (Avoiding `self.binary_op` as that does some redundant layout computation.)
-                    let res = self.wrapping_binary_op(
+                    let res = self.binary_op(
                         mir::BinOp::Eq,
                         &discr,
                         &ImmTy::from_uint(const_int, discr.layout),
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 2bd4d9d..e36d301 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -434,6 +434,11 @@
                 found_bytes: has.bytes()
             },
         );
+        // Make sure this is non-null. We checked dereferenceability above, but if `size` is zero
+        // that does not imply non-null.
+        if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? {
+            throw_validation_failure!(self.path, NullPtr { ptr_kind })
+        }
         // Do not allow pointers to uninhabited types.
         if place.layout.abi.is_uninhabited() {
             let ty = place.layout.ty;
@@ -456,8 +461,8 @@
             // `!` is a ZST and we want to validate it.
             if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
                 let mut skip_recursive_check = false;
-                let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
-                if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) {
+                if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id)
+                {
                     let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() };
                     // Special handling for pointers to statics (irrespective of their type).
                     assert!(!self.ecx.tcx.is_thread_local_static(did));
@@ -495,6 +500,7 @@
                 // If this allocation has size zero, there is no actual mutability here.
                 let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id);
                 if size != Size::ZERO {
+                    let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
                     // Mutable pointer to immutable memory is no good.
                     if ptr_expected_mutbl == Mutability::Mut
                         && alloc_actual_mutbl == Mutability::Not
@@ -831,6 +837,8 @@
         trace!("visit_value: {:?}, {:?}", *op, op.layout);
 
         // Check primitive types -- the leaves of our recursive descent.
+        // We assume that the Scalar validity range does not restrict these values
+        // any further than `try_visit_primitive` does!
         if self.try_visit_primitive(op)? {
             return Ok(());
         }
diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl
index 5b39248..31837e0 100644
--- a/compiler/rustc_driver_impl/messages.ftl
+++ b/compiler/rustc_driver_impl/messages.ftl
@@ -10,6 +10,8 @@
 driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}`
 driver_impl_ice_version = rustc {$version} running on {$triple}
 
+driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}`
+
 driver_impl_rlink_empty_version_number = The input does not contain version number
 
 driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index ba6b9ef..5532eff 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -96,7 +96,7 @@
 
 use crate::session_diagnostics::{
     RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
-    RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead,
+    RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead,
 };
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
@@ -645,8 +645,7 @@
                 match err {
                     CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType),
                     CodegenErrors::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber),
-                    CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => sess
-                        .dcx()
+                    CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => dcx
                         .emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }),
                     CodegenErrors::RustcVersionMismatch { rustc_version } => {
                         dcx.emit_fatal(RLinkRustcVersionMismatch {
@@ -654,6 +653,9 @@
                             current_version: sess.cfg_version,
                         })
                     }
+                    CodegenErrors::CorruptFile => {
+                        dcx.emit_fatal(RlinkCorruptFile { file });
+                    }
                 };
             }
         };
diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs
index 1a9683e..449878f 100644
--- a/compiler/rustc_driver_impl/src/session_diagnostics.rs
+++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs
@@ -33,6 +33,12 @@
 pub(crate) struct RlinkNotAFile;
 
 #[derive(Diagnostic)]
+#[diag(driver_impl_rlink_corrupt_file)]
+pub(crate) struct RlinkCorruptFile<'a> {
+    pub file: &'a std::path::Path,
+}
+
+#[derive(Diagnostic)]
 #[diag(driver_impl_ice)]
 pub(crate) struct Ice;
 
diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs
index a805c4f..08d4a03 100644
--- a/compiler/rustc_expand/src/mbe.rs
+++ b/compiler/rustc_expand/src/mbe.rs
@@ -68,12 +68,15 @@
 /// `MetaVarExpr` are "first-class" token trees. Useful for parsing macros.
 #[derive(Debug, PartialEq, Encodable, Decodable)]
 enum TokenTree {
+    /// A token. Unlike `tokenstream::TokenTree::Token` this lacks a `Spacing`.
+    /// See the comments about `Spacing` in the `transcribe` function.
     Token(Token),
     /// A delimited sequence, e.g. `($e:expr)` (RHS) or `{ $e }` (LHS).
     Delimited(DelimSpan, DelimSpacing, Delimited),
     /// A kleene-style repetition sequence, e.g. `$($e:expr)*` (RHS) or `$($e),*` (LHS).
     Sequence(DelimSpan, SequenceRepetition),
-    /// e.g., `$var`.
+    /// e.g., `$var`. The span covers the leading dollar and the ident. (The span within the ident
+    /// only covers the ident, e.g. `var`.)
     MetaVar(Span, Ident),
     /// e.g., `$var:expr`. Only appears on the LHS.
     MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index d3ea48e..8ad7cb1 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -62,7 +62,10 @@
         match tree {
             TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
                 let span = match trees.next() {
-                    Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => {
+                    Some(&tokenstream::TokenTree::Token(
+                        Token { kind: token::Colon, span: colon_span },
+                        _,
+                    )) => {
                         match trees.next() {
                             Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
                                 Some((fragment, _)) => {
@@ -126,10 +129,12 @@
                                 }
                                 _ => token.span,
                             },
-                            tree => tree.map_or(span, tokenstream::TokenTree::span),
+                            Some(tree) => tree.span(),
+                            None => colon_span,
                         }
                     }
-                    tree => tree.map_or(start_sp, tokenstream::TokenTree::span),
+                    Some(tree) => tree.span(),
+                    None => start_sp,
                 };
 
                 result.push(TokenTree::MetaVarDecl(span, ident, None));
@@ -176,7 +181,7 @@
     // Depending on what `tree` is, we could be parsing different parts of a macro
     match tree {
         // `tree` is a `$` token. Look at the next token in `trees`
-        &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => {
+        &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span: dollar_span }, _) => {
             // FIXME: Handle `Invisible`-delimited groups in a more systematic way
             // during parsing.
             let mut next = outer_trees.next();
@@ -209,7 +214,7 @@
                                         err.emit();
                                         // Returns early the same read `$` to avoid spanning
                                         // unrelated diagnostics that could be performed afterwards
-                                        return TokenTree::token(token::Dollar, span);
+                                        return TokenTree::token(token::Dollar, dollar_span);
                                     }
                                     Ok(elem) => {
                                         maybe_emit_macro_metavar_expr_feature(
@@ -251,7 +256,7 @@
                 // special metavariable that names the crate of the invocation.
                 Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => {
                     let (ident, is_raw) = token.ident().unwrap();
-                    let span = ident.span.with_lo(span.lo());
+                    let span = ident.span.with_lo(dollar_span.lo());
                     if ident.name == kw::Crate && matches!(is_raw, IdentIsRaw::No) {
                         TokenTree::token(token::Ident(kw::DollarCrate, is_raw), span)
                     } else {
@@ -260,16 +265,19 @@
                 }
 
                 // `tree` is followed by another `$`. This is an escaped `$`.
-                Some(&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => {
+                Some(&tokenstream::TokenTree::Token(
+                    Token { kind: token::Dollar, span: dollar_span2 },
+                    _,
+                )) => {
                     if parsing_patterns {
                         span_dollar_dollar_or_metavar_in_the_lhs_err(
                             sess,
-                            &Token { kind: token::Dollar, span },
+                            &Token { kind: token::Dollar, span: dollar_span2 },
                         );
                     } else {
-                        maybe_emit_macro_metavar_expr_feature(features, sess, span);
+                        maybe_emit_macro_metavar_expr_feature(features, sess, dollar_span2);
                     }
-                    TokenTree::token(token::Dollar, span)
+                    TokenTree::token(token::Dollar, dollar_span2)
                 }
 
                 // `tree` is followed by some other token. This is an error.
@@ -281,7 +289,7 @@
                 }
 
                 // There are no more tokens. Just return the `$` we already have.
-                None => TokenTree::token(token::Dollar, span),
+                None => TokenTree::token(token::Dollar, dollar_span),
             }
         }
 
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 3901b82..8a084dc 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -253,8 +253,23 @@
             mbe::TokenTree::MetaVar(mut sp, mut original_ident) => {
                 // Find the matched nonterminal from the macro invocation, and use it to replace
                 // the meta-var.
+                //
+                // We use `Spacing::Alone` everywhere here, because that's the conservative choice
+                // and spacing of declarative macros is tricky. E.g. in this macro:
+                // ```
+                // macro_rules! idents {
+                //     ($($a:ident,)*) => { stringify!($($a)*) }
+                // }
+                // ```
+                // `$a` has no whitespace after it and will be marked `JointHidden`. If you then
+                // call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
+                // if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
+                // producing "xyz", which is bad because it effectively merges tokens.
+                // `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
+                // some of the unnecessary whitespace.
                 let ident = MacroRulesNormalizedIdent::new(original_ident);
                 if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
+                    // njn: explain the use of alone here
                     let tt = match cur_matched {
                         MatchedSingle(ParseNtResult::Tt(tt)) => {
                             // `tt`s are emitted into the output stream directly as "raw tokens",
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 1f3547c..ec7e441 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -309,10 +309,10 @@
         use rustc_ast::token::*;
 
         // The code below is conservative, using `token_alone`/`Spacing::Alone`
-        // in most places. When the resulting code is pretty-printed by
-        // `print_tts` it ends up with spaces between most tokens, which is
-        // safe but ugly. It's hard in general to do better when working at the
-        // token level.
+        // in most places. It's hard in general to do better when working at
+        // the token level. When the resulting code is pretty-printed by
+        // `print_tts` the `space_between` function helps avoid a lot of
+        // unnecessary whitespace, so the results aren't too bad.
         let (tree, rustc) = self;
         match tree {
             TokenTree::Punct(Punct { ch, joint, span }) => {
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 3edea01..cf492a2 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -371,9 +371,9 @@
     .suggestion = cast the value to `{$cast_ty}`
     .help = cast the value to `{$cast_ty}`
 
-hir_analysis_pattern_type_non_const_range = "range patterns must have constant range start and end"
-hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pattern types"
-    .label = "this type is the same as the inner type without a pattern"
+hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end
+hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types
+    .label = this type is the same as the inner type without a pattern
 hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
     .label = not allowed in type signatures
 
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 4540310..e51f95e 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -6,7 +6,7 @@
 //!
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
 
-use rustc_ast::visit::walk_list;
+use rustc_ast::visit::visit_opt;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -168,7 +168,7 @@
                 hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
             }
         }
-        walk_list!(visitor, visit_expr, &blk.expr);
+        visit_opt!(visitor, visit_expr, &blk.expr);
     }
 
     visitor.cx = prev_cx;
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 168a370..9c687d3 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -1382,7 +1382,7 @@
         span: Span,
     },
     SelfTyParam(Span),
-    TyParam(DefId),
+    Param(DefId),
     DefVariant,
     None,
 }
@@ -1498,11 +1498,11 @@
         GenericsArgsErrExtend::DefVariant => {
             err.note("enum variants can't have type parameters");
         }
-        GenericsArgsErrExtend::TyParam(def_id) => {
-            if let Some(span) = tcx.def_ident_span(def_id) {
-                let name = tcx.item_name(def_id);
-                err.span_note(span, format!("type parameter `{name}` defined here"));
-            }
+        GenericsArgsErrExtend::Param(def_id) => {
+            let span = tcx.def_ident_span(def_id).unwrap();
+            let kind = tcx.def_descr(def_id);
+            let name = tcx.item_name(def_id);
+            err.span_note(span, format!("{kind} `{name}` defined here"));
         }
         GenericsArgsErrExtend::SelfTyParam(span) => {
             err.span_suggestion_verbose(
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 0cd77fe..4b1c0da 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -1758,7 +1758,7 @@
                 assert_eq!(opt_self_ty, None);
                 let _ = self.prohibit_generic_args(
                     path.segments.iter(),
-                    GenericsArgsErrExtend::TyParam(def_id),
+                    GenericsArgsErrExtend::Param(def_id),
                 );
                 self.lower_ty_param(hir_id)
             }
@@ -2191,10 +2191,15 @@
 
                                 hir::ExprKind::Path(hir::QPath::Resolved(
                                     _,
-                                    &hir::Path {
-                                        res: Res::Def(DefKind::ConstParam, def_id), ..
+                                    path @ &hir::Path {
+                                        res: Res::Def(DefKind::ConstParam, def_id),
+                                        ..
                                     },
                                 )) => {
+                                    let _ = self.prohibit_generic_args(
+                                        path.segments.iter(),
+                                        GenericsArgsErrExtend::Param(def_id),
+                                    );
                                     let ty = tcx
                                         .type_of(def_id)
                                         .no_bound_vars()
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 33c2443..11a1c65 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1159,7 +1159,7 @@
             let sig = self
                 .at(cause, self.param_env)
                 .trace(prev_ty, new_ty)
-                .lub(DefineOpaqueTypes::No, a_sig, b_sig)
+                .lub(DefineOpaqueTypes::Yes, a_sig, b_sig)
                 .map(|ok| self.register_infer_ok_obligations(ok))?;
 
             // Reify both sides and return the reified fn pointer type.
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 9d16f0d..e29a410 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -204,6 +204,60 @@
             fake_reads: Default::default(),
         };
 
+        let _ = euv::ExprUseVisitor::new(
+            &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id),
+            &mut delegate,
+        )
+        .consume_body(body);
+
+        // There are several curious situations with coroutine-closures where
+        // analysis is too aggressive with borrows when the coroutine-closure is
+        // marked `move`. Specifically:
+        //
+        // 1. If the coroutine-closure was inferred to be `FnOnce` during signature
+        // inference, then it's still possible that we try to borrow upvars from
+        // the coroutine-closure because they are not used by the coroutine body
+        // in a way that forces a move. See the test:
+        // `async-await/async-closures/force-move-due-to-inferred-kind.rs`.
+        //
+        // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it
+        // uses its upvars, but not *all* upvars would force the closure to `FnOnce`.
+        // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`.
+        //
+        // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
+        // coroutine bodies can't borrow from their parent closure. To fix this,
+        // we force the inner coroutine to also be `move`. This only matters for
+        // coroutine-closures that are `move` since otherwise they themselves will
+        // be borrowing from the outer environment, so there's no self-borrows occuring.
+        //
+        // One *important* note is that we do a call to `process_collected_capture_information`
+        // to eagerly test whether the coroutine would end up `FnOnce`, but we do this
+        // *before* capturing all the closure args by-value below, since that would always
+        // cause the analysis to return `FnOnce`.
+        if let UpvarArgs::Coroutine(..) = args
+            && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) =
+                self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind")
+            && let parent_hir_id =
+                self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id))
+            && let parent_ty = self.node_ty(parent_hir_id)
+            && let hir::CaptureBy::Value { move_kw } =
+                self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause
+        {
+            // (1.) Closure signature inference forced this closure to `FnOnce`.
+            if let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) {
+                capture_clause = hir::CaptureBy::Value { move_kw };
+            }
+            // (2.) The way that the closure uses its upvars means it's `FnOnce`.
+            else if let (_, ty::ClosureKind::FnOnce, _) = self
+                .process_collected_capture_information(
+                    capture_clause,
+                    &delegate.capture_information,
+                )
+            {
+                capture_clause = hir::CaptureBy::Value { move_kw };
+            }
+        }
+
         // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
         // to `ByRef` for the `async {}` block internal to async fns/closure. This means
         // that we would *not* be moving all of the parameters into the async block by default.
@@ -253,34 +307,6 @@
             }
         }
 
-        let _ = euv::ExprUseVisitor::new(
-            &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id),
-            &mut delegate,
-        )
-        .consume_body(body);
-
-        // If a coroutine is comes from a coroutine-closure that is `move`, but
-        // the coroutine-closure was inferred to be `FnOnce` during signature
-        // inference, then it's still possible that we try to borrow upvars from
-        // the coroutine-closure because they are not used by the coroutine body
-        // in a way that forces a move.
-        //
-        // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
-        // coroutine bodies can't borrow from their parent closure. To fix this,
-        // we force the inner coroutine to also be `move`. This only matters for
-        // coroutine-closures that are `move` since otherwise they themselves will
-        // be borrowing from the outer environment, so there's no self-borrows occuring.
-        if let UpvarArgs::Coroutine(..) = args
-            && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) =
-                self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind")
-            && let parent_hir_id =
-                self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id))
-            && let parent_ty = self.node_ty(parent_hir_id)
-            && let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty)
-        {
-            capture_clause = self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause;
-        }
-
         debug!(
             "For closure={:?}, capture_information={:#?}",
             closure_def_id, delegate.capture_information
@@ -289,7 +315,7 @@
         self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
 
         let (capture_information, closure_kind, origin) = self
-            .process_collected_capture_information(capture_clause, delegate.capture_information);
+            .process_collected_capture_information(capture_clause, &delegate.capture_information);
 
         self.compute_min_captures(closure_def_id, capture_information, span);
 
@@ -545,13 +571,14 @@
     fn process_collected_capture_information(
         &self,
         capture_clause: hir::CaptureBy,
-        capture_information: InferredCaptureInformation<'tcx>,
+        capture_information: &InferredCaptureInformation<'tcx>,
     ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) {
         let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM;
         let mut origin: Option<(Span, Place<'tcx>)> = None;
 
         let processed = capture_information
-            .into_iter()
+            .iter()
+            .cloned()
             .map(|(place, mut capture_info)| {
                 // Apply rules for safety before inferring closure kind
                 let (place, capture_kind) =
diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl
index e74173b..de2177e 100644
--- a/compiler/rustc_incremental/messages.ftl
+++ b/compiler/rustc_incremental/messages.ftl
@@ -21,6 +21,8 @@
 incremental_copy_workproduct_to_cache =
     error copying object file `{$from}` to incremental directory as `{$to}`: {$err}
 
+incremental_corrupt_file = corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant.
+
 incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err}
 
 incremental_create_incr_comp_dir =
diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs
index 61bb035..e94a7fb 100644
--- a/compiler/rustc_incremental/src/errors.rs
+++ b/compiler/rustc_incremental/src/errors.rs
@@ -306,3 +306,9 @@
     pub path: &'a Path,
     pub err: std::io::Error,
 }
+
+#[derive(Diagnostic)]
+#[diag(incremental_corrupt_file)]
+pub struct CorruptFile<'a> {
+    pub path: &'a Path,
+}
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 26aaa247..9e6ce06 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -115,7 +115,12 @@
 
         if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
             // Decode the list of work_products
-            let mut work_product_decoder = MemDecoder::new(&work_products_data[..], start_pos);
+            let Ok(mut work_product_decoder) =
+                MemDecoder::new(&work_products_data[..], start_pos)
+            else {
+                sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
+                return LoadResult::DataOutOfDate;
+            };
             let work_products: Vec<SerializedWorkProduct> =
                 Decodable::decode(&mut work_product_decoder);
 
@@ -145,7 +150,10 @@
         LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
         LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
         LoadResult::Ok { data: (bytes, start_pos) } => {
-            let mut decoder = MemDecoder::new(&bytes, start_pos);
+            let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else {
+                sess.dcx().emit_warn(errors::CorruptFile { path: &path });
+                return LoadResult::DataOutOfDate;
+            };
             let prev_commandline_args_hash = u64::decode(&mut decoder);
 
             if prev_commandline_args_hash != expected_hash {
@@ -181,9 +189,14 @@
 
     let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
 
-    match load_data(&query_cache_path(sess), sess) {
+    let path = query_cache_path(sess);
+    match load_data(&path, sess) {
         LoadResult::Ok { data: (bytes, start_pos) } => {
-            Some(OnDiskCache::new(sess, bytes, start_pos))
+            let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| {
+                sess.dcx().emit_warn(errors::CorruptFile { path: &path });
+                OnDiskCache::new_empty(sess.source_map())
+            });
+            Some(cache)
         }
         _ => Some(OnDiskCache::new_empty(sess.source_map())),
     }
diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl
index 8f1c4ad..fbe8d31 100644
--- a/compiler/rustc_infer/messages.ftl
+++ b/compiler/rustc_infer/messages.ftl
@@ -104,10 +104,10 @@
 infer_consider_specifying_length = consider specifying the actual array length
 infer_data_flows = ...but data{$label_var1_exists ->
     [true] {" "}from `{$label_var1}`
-    *[false] -> {""}
+    *[false] {""}
 } flows{$label_var2_exists ->
     [true] {" "}into `{$label_var2}`
-    *[false] -> {""}
+    *[false] {""}
 } here
 
 infer_data_lifetime_flow = ...but data with one lifetime flows into the other here
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 46a7e7b..e0894ed 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -173,7 +173,10 @@
 
         ty::ReError(_) => return,
 
-        ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => {
+        // FIXME(#125431): `ReVar` shouldn't reach here.
+        ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
+
+        ty::ReBound(..) | ty::ReErased => {
             bug!("unexpected region for note_and_explain_region: {:?}", region);
         }
     };
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 76d5d7a..f1f9b74 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -458,7 +458,7 @@
                 }
             }
 
-            for &cnum in tcx.crates(()) {
+            for &cnum in tcx.crates_including_speculative(()) {
                 let source = tcx.used_crate_source(cnum);
                 if let Some((path, _)) = &source.dylib {
                     files.push(escape_dep_filename(&path.display().to_string()));
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 6f6480a..a9304f2 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -263,7 +263,7 @@
     .help = the default ABI is {$default_abi}
 
 lint_for_loops_over_fallibles =
-    for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
+    for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement
     .suggestion = consider using `if let` to clear intent
     .remove_next = to iterate over `{$recv_snip}` remove the call to `next`
     .use_while_let = to check pattern in a loop use `while let`
@@ -627,7 +627,7 @@
     .label = pattern not allowed in foreign function
 
 lint_private_extern_crate_reexport =
-    extern crate `{$ident}` is private, and cannot be re-exported (error E0365), consider declaring with `pub`
+    extern crate `{$ident}` is private, and cannot be re-exported, consider declaring with `pub`
 
 lint_proc_macro_back_compat = using an old version of `{$crate_name}`
     .note = older versions of the `{$crate_name}` crate will stop compiling in future versions of Rust; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives
diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
index a6876d8..b05f5e7 100644
--- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
@@ -52,14 +52,27 @@
 
         let ty = cx.typeck_results().expr_ty(arg);
 
-        let &ty::Adt(adt, args) = ty.kind() else { return };
+        let (adt, args, ref_mutability) = match ty.kind() {
+            &ty::Adt(adt, args) => (adt, args, None),
+            &ty::Ref(_, ty, mutability) => match ty.kind() {
+                &ty::Adt(adt, args) => (adt, args, Some(mutability)),
+                _ => return,
+            },
+            _ => return,
+        };
 
         let (article, ty, var) = match adt.did() {
+            did if cx.tcx.is_diagnostic_item(sym::Option, did) && ref_mutability.is_some() => ("a", "Option", "Some"),
             did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
             did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
             _ => return,
         };
 
+        let ref_prefix = match ref_mutability {
+            None => "",
+            Some(ref_mutability) => ref_mutability.ref_prefix_str(),
+        };
+
         let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
             && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
         {
@@ -85,7 +98,7 @@
         cx.emit_span_lint(
             FOR_LOOPS_OVER_FALLIBLES,
             arg.span,
-            ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
+            ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion },
         );
     }
 }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 3bd6fac..64c0939 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -620,6 +620,7 @@
 #[diag(lint_for_loops_over_fallibles)]
 pub struct ForLoopsOverFalliblesDiag<'a> {
     pub article: &'static str,
+    pub ref_prefix: &'static str,
     pub ty: &'static str,
     #[subdiagnostic]
     pub sub: ForLoopsOverFalliblesLoopSub<'a>,
@@ -2227,7 +2228,7 @@
 pub struct UnusedMacroUse;
 
 #[derive(LintDiagnostic)]
-#[diag(lint_private_extern_crate_reexport)]
+#[diag(lint_private_extern_crate_reexport, code = E0365)]
 pub struct PrivateExternCrateReexport {
     pub ident: Ident,
 }
diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs
index 81257f9..e11c45b 100644
--- a/compiler/rustc_log/src/lib.rs
+++ b/compiler/rustc_log/src/lib.rs
@@ -159,7 +159,9 @@
         if !target.contains(&self.backtrace_target) {
             return Ok(());
         }
-        let backtrace = std::backtrace::Backtrace::capture();
+        // Use Backtrace::force_capture because we don't want to depend on the
+        // RUST_BACKTRACE environment variable being set.
+        let backtrace = std::backtrace::Backtrace::force_capture();
         writeln!(writer, "stack backtrace: \n{backtrace:?}")
     }
 }
diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs
index 5fa11d2..7b5dd16 100644
--- a/compiler/rustc_macros/src/serialize.rs
+++ b/compiler/rustc_macros/src/serialize.rs
@@ -13,7 +13,7 @@
         quote! {}
     };
 
-    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound });
+    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound });
     s.add_bounds(synstructure::AddBounds::Fields);
     s.underscore_const(true);
 
@@ -34,7 +34,7 @@
 
 pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let decoder_ty = quote! { __D };
-    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_span::SpanDecoder});
+    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_span::SpanDecoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
@@ -43,7 +43,7 @@
 
 pub fn decodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let decoder_ty = quote! { __D };
-    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder});
+    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_serialize::Decoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
@@ -120,7 +120,7 @@
     let __decoder = quote! { __decoder };
     // Use the span of the field for the method call, so
     // that backtraces will point to the field.
-    quote_spanned! {field_span=> #decode_inner_method(#__decoder) }
+    quote_spanned! { field_span=> #decode_inner_method(#__decoder) }
 }
 
 pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
@@ -133,7 +133,7 @@
     };
 
     let encoder_ty = quote! { __E };
-    s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound });
+    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound });
     s.add_bounds(synstructure::AddBounds::Fields);
     s.underscore_const(true);
 
@@ -142,7 +142,7 @@
 
 pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
-        s.add_impl_generic(parse_quote! {'tcx});
+        s.add_impl_generic(parse_quote! { 'tcx });
     }
     s.add_impl_generic(parse_quote! { '__a });
     let encoder_ty = quote! { EncodeContext<'__a, 'tcx> };
@@ -154,7 +154,7 @@
 
 pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let encoder_ty = quote! { __E };
-    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder});
+    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
@@ -163,7 +163,7 @@
 
 pub fn encodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let encoder_ty = quote! { __E };
-    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder});
+    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs
index 99181f9..8afeb43 100644
--- a/compiler/rustc_metadata/src/dependency_format.rs
+++ b/compiler/rustc_metadata/src/dependency_format.rs
@@ -143,7 +143,7 @@
                     && sess.crt_static(Some(ty))
                     && !sess.target.crt_static_allows_dylibs)
             {
-                for &cnum in tcx.crates(()).iter() {
+                for &cnum in tcx.used_crates(()).iter() {
                     if tcx.dep_kind(cnum).macros_only() {
                         continue;
                     }
@@ -164,7 +164,7 @@
     // Sweep all crates for found dylibs. Add all dylibs, as well as their
     // dependencies, ensuring there are no conflicts. The only valid case for a
     // dependency to be relied upon twice is for both cases to rely on a dylib.
-    for &cnum in tcx.crates(()).iter() {
+    for &cnum in tcx.used_crates(()).iter() {
         if tcx.dep_kind(cnum).macros_only() {
             continue;
         }
@@ -182,7 +182,7 @@
     }
 
     // Collect what we've got so far in the return vector.
-    let last_crate = tcx.crates(()).len();
+    let last_crate = tcx.used_crates(()).len();
     let mut ret = (1..last_crate + 1)
         .map(|cnum| match formats.get(&CrateNum::new(cnum)) {
             Some(&RequireDynamic) => Linkage::Dynamic,
@@ -196,7 +196,7 @@
     //
     // If the crate hasn't been included yet and it's not actually required
     // (e.g., it's an allocator) then we skip it here as well.
-    for &cnum in tcx.crates(()).iter() {
+    for &cnum in tcx.used_crates(()).iter() {
         let src = tcx.used_crate_source(cnum);
         if src.dylib.is_none()
             && !formats.contains_key(&cnum)
@@ -284,7 +284,7 @@
 
 fn attempt_static(tcx: TyCtxt<'_>, unavailable: &mut Vec<CrateNum>) -> Option<DependencyList> {
     let all_crates_available_as_rlib = tcx
-        .crates(())
+        .used_crates(())
         .iter()
         .copied()
         .filter_map(|cnum| {
@@ -305,7 +305,7 @@
     // All crates are available in an rlib format, so we're just going to link
     // everything in explicitly so long as it's actually required.
     let mut ret = tcx
-        .crates(())
+        .used_crates(())
         .iter()
         .map(|&cnum| match tcx.dep_kind(cnum) {
             CrateDepKind::Explicit => Linkage::Static,
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 7de03be..6ff1997 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -853,7 +853,12 @@
             slice_owned(mmap, Deref::deref)
         }
     };
-    let blob = MetadataBlob(raw_bytes);
+    let Ok(blob) = MetadataBlob::new(raw_bytes) else {
+        return Err(MetadataError::LoadFailure(format!(
+            "corrupt metadata encountered in {}",
+            filename.display()
+        )));
+    };
     match blob.check_compatibility(cfg_version) {
         Ok(()) => Ok(blob),
         Err(None) => Err(MetadataError::LoadFailure(format!(
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index bb68c6e..f91e121 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -40,10 +40,9 @@
 mod cstore_impl;
 
 /// A reference to the raw binary version of crate metadata.
-/// A `MetadataBlob` internally is just a reference counted pointer to
-/// the actual data, so cloning it is cheap.
-#[derive(Clone)]
-pub(crate) struct MetadataBlob(pub(crate) OwnedSlice);
+/// This struct applies [`MemDecoder`]'s validation when constructed
+/// so that later constructions are guaranteed to succeed.
+pub(crate) struct MetadataBlob(OwnedSlice);
 
 impl std::ops::Deref for MetadataBlob {
     type Target = [u8];
@@ -54,6 +53,19 @@
     }
 }
 
+impl MetadataBlob {
+    /// Runs the [`MemDecoder`] validation and if it passes, constructs a new [`MetadataBlob`].
+    pub fn new(slice: OwnedSlice) -> Result<Self, ()> {
+        if MemDecoder::new(&slice, 0).is_ok() { Ok(Self(slice)) } else { Err(()) }
+    }
+
+    /// Since this has passed the validation of [`MetadataBlob::new`], this returns bytes which are
+    /// known to pass the [`MemDecoder`] validation.
+    pub fn bytes(&self) -> &OwnedSlice {
+        &self.0
+    }
+}
+
 /// A map from external crate numbers (as decoded from some crate file) to
 /// local crate numbers (as generated during this session). Each external
 /// crate may refer to types in other external crates, and each has their
@@ -165,7 +177,14 @@
     fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> {
         let tcx = self.tcx();
         DecodeContext {
-            opaque: MemDecoder::new(self.blob(), pos),
+            // FIXME: This unwrap should never panic because we check that it won't when creating
+            // `MetadataBlob`. Ideally we'd just have a `MetadataDecoder` and hand out subslices of
+            // it as we do elsewhere in the compiler using `MetadataDecoder::split_at`. But we own
+            // the data for the decoder so holding onto the `MemDecoder` too would make us a
+            // self-referential struct which is downright goofy because `MetadataBlob` is already
+            // self-referential. Probably `MemDecoder` should contain an `OwnedSlice`, but that
+            // demands a significant refactoring due to our crate graph.
+            opaque: MemDecoder::new(self.blob(), pos).unwrap(),
             cdata: self.cdata(),
             blob: self.blob(),
             sess: self.sess().or(tcx.map(|tcx| tcx.sess)),
@@ -393,7 +412,7 @@
     where
         F: FnOnce(&mut Self) -> R,
     {
-        let new_opaque = MemDecoder::new(self.opaque.data(), pos);
+        let new_opaque = self.opaque.split_at(pos);
         let old_opaque = mem::replace(&mut self.opaque, new_opaque);
         let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode);
         let r = f(self);
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index c783149..4cad317 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -435,7 +435,7 @@
             // traversal, but not globally minimal across all crates.
             let bfs_queue = &mut VecDeque::new();
 
-            for &cnum in tcx.crates(()) {
+            for &cnum in tcx.crates_including_speculative(()) {
                 // Ignore crates without a corresponding local `extern crate` item.
                 if tcx.missing_extern_crate_item(cnum) {
                     continue;
@@ -505,7 +505,7 @@
             tcx.arena
                 .alloc_slice(&CStore::from_tcx(tcx).crate_dependencies_in_postorder(LOCAL_CRATE))
         },
-        crates: |tcx, ()| {
+        crates_including_speculative: |tcx, ()| {
             // The list of loaded crates is now frozen in query cache,
             // so make sure cstore is not mutably accessed from here on.
             tcx.untracked().cstore.freeze();
diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs
index 9950bc1..861bf6b 100644
--- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs
+++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs
@@ -48,7 +48,7 @@
     fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefPathHashMapRef<'static> {
         let len = d.read_usize();
         let pos = d.position();
-        let o = d.blob().clone().0.slice(|blob| &blob[pos..pos + len]);
+        let o = d.blob().bytes().clone().slice(|blob| &blob[pos..pos + len]);
 
         // Although we already have the data we need via the `OwnedSlice`, we still need
         // to advance the `DecodeContext`'s position so it's in a valid state after
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index db0dc6d..fbe70d4 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1898,7 +1898,7 @@
 
         let deps = self
             .tcx
-            .crates(())
+            .crates_including_speculative(())
             .iter()
             .map(|&cnum| {
                 let dep = CrateDep {
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index f3f24f7..b17af47 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -1016,7 +1016,7 @@
     let krate = tcx.hir_crate(());
     let hir_body_hash = krate.opt_hir_hash.expect("HIR hash missing while computing crate hash");
 
-    let upstream_crates = upstream_crates(tcx);
+    let upstream_crates = upstream_crates_for_hashing(tcx);
 
     let resolutions = tcx.resolutions(());
 
@@ -1085,9 +1085,9 @@
     Svh::new(crate_hash)
 }
 
-fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> {
+fn upstream_crates_for_hashing(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> {
     let mut upstream_crates: Vec<_> = tcx
-        .crates(())
+        .crates_including_speculative(())
         .iter()
         .map(|&cnum| {
             let stable_crate_id = tcx.stable_crate_id(cnum);
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 6e152cb..eabbcc2 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -1,19 +1,22 @@
-use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar};
+use std::borrow::Cow;
+use std::{any::Any, backtrace::Backtrace, fmt};
 
-use crate::error;
-use crate::mir::{ConstAlloc, ConstValue};
-use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree};
+use either::Either;
 
 use rustc_ast_ir::Mutability;
 use rustc_data_structures::sync::Lock;
 use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_session::CtfeBacktrace;
+use rustc_span::Symbol;
 use rustc_span::{def_id::DefId, Span, DUMMY_SP};
 use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange};
 
-use std::borrow::Cow;
-use std::{any::Any, backtrace::Backtrace, fmt};
+use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar};
+
+use crate::error;
+use crate::mir::{ConstAlloc, ConstValue};
+use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree};
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 pub enum ErrorHandled {
@@ -63,6 +66,9 @@
     pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo {
         ReportedErrorInfo { is_tainted_by_errors: true, error }
     }
+    pub fn is_tainted_by_errors(&self) -> bool {
+        self.is_tainted_by_errors
+    }
 }
 
 impl From<ErrorGuaranteed> for ReportedErrorInfo {
@@ -310,6 +316,10 @@
     RemainderOverflow,
     /// Overflowing inbounds pointer arithmetic.
     PointerArithOverflow,
+    /// Overflow in arithmetic that may not overflow.
+    ArithOverflow { intrinsic: Symbol },
+    /// Shift by too much.
+    ShiftOverflow { intrinsic: Symbol, shift_amount: Either<u128, i128> },
     /// Invalid metadata in a wide pointer
     InvalidMeta(InvalidMetaKind),
     /// Reading a C string that does not end within its allocation.
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index e1ae2e0..fbc7d3a 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -295,11 +295,13 @@
 }
 
 impl BinOp {
-    pub fn to_hir_binop(self) -> hir::BinOpKind {
+    pub(crate) fn to_hir_binop(self) -> hir::BinOpKind {
         match self {
-            BinOp::Add => hir::BinOpKind::Add,
-            BinOp::Sub => hir::BinOpKind::Sub,
-            BinOp::Mul => hir::BinOpKind::Mul,
+            // HIR `+`/`-`/`*` can map to either of these MIR BinOp, depending
+            // on whether overflow checks are enabled or not.
+            BinOp::Add | BinOp::AddWithOverflow => hir::BinOpKind::Add,
+            BinOp::Sub | BinOp::SubWithOverflow => hir::BinOpKind::Sub,
+            BinOp::Mul | BinOp::MulWithOverflow => hir::BinOpKind::Mul,
             BinOp::Div => hir::BinOpKind::Div,
             BinOp::Rem => hir::BinOpKind::Rem,
             BinOp::BitXor => hir::BinOpKind::BitXor,
@@ -313,10 +315,8 @@
             BinOp::Gt => hir::BinOpKind::Gt,
             BinOp::Le => hir::BinOpKind::Le,
             BinOp::Ge => hir::BinOpKind::Ge,
+            // We don't have HIR syntax for these.
             BinOp::Cmp
-            | BinOp::AddWithOverflow
-            | BinOp::SubWithOverflow
-            | BinOp::MulWithOverflow
             | BinOp::AddUnchecked
             | BinOp::SubUnchecked
             | BinOp::MulUnchecked
@@ -338,6 +338,11 @@
         })
     }
 
+    /// Returns whether this is a `FooWithOverflow`
+    pub fn is_overflowing(self) -> bool {
+        self.overflowing_to_wrapping().is_some()
+    }
+
     /// If this is a `Foo`, return `Some(FooWithOverflow)`.
     pub fn wrapping_to_overflowing(self) -> Option<BinOp> {
         Some(match self {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index c2f7a22..0654d82 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1860,13 +1860,22 @@
         eval_always
         desc { "calculating the stability index for the local crate" }
     }
-    query crates(_: ()) -> &'tcx [CrateNum] {
+    /// All loaded crates, including those loaded purely for doc links or diagnostics.
+    /// (Diagnostics include lints, so speculatively loaded crates may occur in successful
+    /// compilation even without doc links.)
+    /// Should be used when encoding crate metadata (and therefore when generating crate hash,
+    /// depinfo and similar things), to avoid dangling crate references in other encoded data,
+    /// like source maps.
+    /// May also be used for diagnostics - if we are loading a crate anyway we can suggest some
+    /// items from it as well.
+    /// But otherwise, `used_crates` should generally be used.
+    query crates_including_speculative(_: ()) -> &'tcx [CrateNum] {
         eval_always
         desc { "fetching all foreign CrateNum instances" }
     }
-    // Crates that are loaded non-speculatively (not for diagnostics or doc links).
-    // FIXME: This is currently only used for collecting lang items, but should be used instead of
-    // `crates` in most other cases too.
+    /// Crates that are loaded non-speculatively (not for diagnostics or doc links).
+    /// Should be used to maintain observable language behavior, for example when collecting lang
+    /// items or impls from all crates, or collecting libraries to link.
     query used_crates(_: ()) -> &'tcx [CrateNum] {
         eval_always
         desc { "fetching `CrateNum`s for all crates loaded non-speculatively" }
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 2dcb587..941911c 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -154,24 +154,25 @@
 
 impl<'sess> OnDiskCache<'sess> {
     /// Creates a new `OnDiskCache` instance from the serialized data in `data`.
-    pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Self {
-        debug_assert!(sess.opts.incremental.is_some());
+    ///
+    /// The serialized cache has some basic integrity checks, if those checks indicate that the
+    /// on-disk data is corrupt, an error is returned.
+    pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result<Self, ()> {
+        assert!(sess.opts.incremental.is_some());
 
-        // Wrap in a scope so we can borrow `data`.
-        let footer: Footer = {
-            let mut decoder = MemDecoder::new(&data, start_pos);
+        let mut decoder = MemDecoder::new(&data, start_pos)?;
 
-            // Decode the *position* of the footer, which can be found in the
-            // last 8 bytes of the file.
-            let footer_pos = decoder
-                .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| {
-                    IntEncodedWithFixedSize::decode(decoder).0 as usize
-                });
-            // Decode the file footer, which contains all the lookup tables, etc.
-            decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER))
-        };
+        // Decode the *position* of the footer, which can be found in the
+        // last 8 bytes of the file.
+        let footer_pos = decoder
+            .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| {
+                IntEncodedWithFixedSize::decode(decoder).0 as usize
+            });
+        // Decode the file footer, which contains all the lookup tables, etc.
+        let footer: Footer =
+            decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER));
 
-        Self {
+        Ok(Self {
             serialized_data: RwLock::new(Some(data)),
             file_index_to_stable_id: footer.file_index_to_stable_id,
             file_index_to_file: Default::default(),
@@ -184,7 +185,7 @@
             expn_data: footer.expn_data,
             foreign_expn_data: footer.foreign_expn_data,
             hygiene_context: Default::default(),
-        }
+        })
     }
 
     pub fn new_empty(source_map: &'sess SourceMap) -> Self {
@@ -437,7 +438,8 @@
         let serialized_data = self.serialized_data.read();
         let mut decoder = CacheDecoder {
             tcx,
-            opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()),
+            opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize())
+                .unwrap(),
             source_map: self.source_map,
             file_index_to_file: &self.file_index_to_file,
             file_index_to_stable_id: &self.file_index_to_stable_id,
@@ -558,7 +560,7 @@
     {
         debug_assert!(pos < self.opaque.len());
 
-        let new_opaque = MemDecoder::new(self.opaque.data(), pos);
+        let new_opaque = self.opaque.split_at(pos);
         let old_opaque = mem::replace(&mut self.opaque, new_opaque);
         let r = f(self);
         self.opaque = old_opaque;
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 896114e..e0d1b5a 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1611,7 +1611,7 @@
 
     pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx {
         iter::once(LOCAL_CRATE)
-            .chain(self.crates(()).iter().copied())
+            .chain(self.used_crates(()).iter().copied())
             .flat_map(move |cnum| self.traits(cnum).iter().copied())
     }
 
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index a1ead1b..0bcd02a 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -3258,7 +3258,7 @@
     let queue = &mut Vec::new();
     let mut seen_defs: DefIdSet = Default::default();
 
-    for &cnum in tcx.crates(()).iter() {
+    for &cnum in tcx.crates_including_speculative(()).iter() {
         let def_id = cnum.as_def_id();
 
         // Ignore crates that are not direct dependencies.
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index fc9a854..40f3db8 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -401,6 +401,45 @@
     pub fn coroutine_witness_ty(self) -> Ty<'tcx> {
         self.split().coroutine_witness_ty
     }
+
+    pub fn has_self_borrows(&self) -> bool {
+        match self.coroutine_captures_by_ref_ty().kind() {
+            ty::FnPtr(sig) => sig
+                .skip_binder()
+                .visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST })
+                .is_break(),
+            ty::Error(_) => true,
+            _ => bug!(),
+        }
+    }
+}
+/// Unlike `has_escaping_bound_vars` or `outermost_exclusive_binder`, this will
+/// detect only regions bound *at* the debruijn index.
+struct HasRegionsBoundAt {
+    binder: ty::DebruijnIndex,
+}
+// FIXME: Could be optimized to not walk into components with no escaping bound vars.
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasRegionsBoundAt {
+    type Result = ControlFlow<()>;
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: &ty::Binder<'tcx, T>,
+    ) -> Self::Result {
+        self.binder.shift_in(1);
+        t.super_visit_with(self)?;
+        self.binder.shift_out(1);
+        ControlFlow::Continue(())
+    }
+
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
+        if let ty::ReBound(binder, _) = *r
+            && self.binder == binder
+        {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index cf1cbb9..d2bcab1 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -205,7 +205,7 @@
     // Traits defined in the current crate can't have impls in upstream
     // crates, so we don't bother querying the cstore.
     if !trait_id.is_local() {
-        for &cnum in tcx.crates(()).iter() {
+        for &cnum in tcx.used_crates(()).iter() {
             for &(impl_def_id, simplified_self_ty) in
                 tcx.implementations_of_trait((cnum, trait_id)).iter()
             {
@@ -247,7 +247,7 @@
     let mut impls = Vec::new();
 
     let mut res = Ok(());
-    for cnum in iter::once(LOCAL_CRATE).chain(tcx.crates(()).iter().copied()) {
+    for cnum in iter::once(LOCAL_CRATE).chain(tcx.used_crates(()).iter().copied()) {
         let incoherent_impls = match tcx.crate_incoherent_impls((cnum, simp)) {
             Ok(impls) => impls,
             Err(e) => {
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 0bb44db..67e1e15 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -335,12 +335,12 @@
 mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items
 
 mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe =
-    borrow of layout constrained field with interior mutability is unsafe and requires unsafe block (error E0133)
+    borrow of layout constrained field with interior mutability is unsafe and requires unsafe block
     .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
     .label = borrow of layout constrained field with interior mutability
 
 mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe =
-    call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
+    call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
     .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
         [1] feature
         *[count] features
@@ -355,48 +355,47 @@
     .label = call to function with `#[target_feature]`
 
 mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe =
-    call to unsafe function `{$function}` is unsafe and requires unsafe block (error E0133)
+    call to unsafe function `{$function}` is unsafe and requires unsafe block
     .note = consult the function's documentation for information on how to avoid undefined behavior
     .label = call to unsafe function
 
 mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless =
-    call to unsafe function is unsafe and requires unsafe block (error E0133)
+    call to unsafe function is unsafe and requires unsafe block
     .note = consult the function's documentation for information on how to avoid undefined behavior
     .label = call to unsafe function
 
 mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe =
-    dereference of raw pointer is unsafe and requires unsafe block (error E0133)
+    dereference of raw pointer is unsafe and requires unsafe block
     .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
     .label = dereference of raw pointer
 
 mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe =
-    use of extern static is unsafe and requires unsafe block (error E0133)
+    use of extern static is unsafe and requires unsafe block
     .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
     .label = use of extern static
 
 mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe =
-    initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe
-    block (error E0133)
+    initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block
     .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
     .label = initializing type with `rustc_layout_scalar_valid_range` attr
 
 mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
-    use of inline assembly is unsafe and requires unsafe block (error E0133)
+    use of inline assembly is unsafe and requires unsafe block
     .note = inline assembly is entirely unchecked and can cause undefined behavior
     .label = use of inline assembly
 
 mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe =
-    use of mutable static is unsafe and requires unsafe block (error E0133)
+    use of mutable static is unsafe and requires unsafe block
     .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
     .label = use of mutable static
 
 mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe =
-    mutation of layout constrained field is unsafe and requires unsafe block (error E0133)
+    mutation of layout constrained field is unsafe and requires unsafe block
     .note = mutating layout constrained fields cannot statically be checked for valid values
     .label = mutation of layout constrained field
 
 mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe =
-    access to union field is unsafe and requires unsafe block (error E0133)
+    access to union field is unsafe and requires unsafe block
     .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
     .label = access to union field
 
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 3fc7193..d948354 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -925,7 +925,7 @@
                 for subpattern in prefix.iter() {
                     self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f);
                 }
-                for subpattern in slice {
+                if let Some(subpattern) = slice {
                     self.visit_primary_bindings(
                         subpattern,
                         pattern_user_ty.clone().subslice(from, to),
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index e2a2846..38e6c00 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -21,7 +21,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
     #[label]
@@ -32,7 +32,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
     #[label]
@@ -42,7 +42,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
     #[label]
@@ -52,7 +52,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
     #[label]
@@ -62,7 +62,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
     #[label]
@@ -72,7 +72,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
     #[label]
@@ -82,7 +82,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
     #[label]
@@ -92,7 +92,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
     #[label]
@@ -102,7 +102,10 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe)]
+#[diag(
+    mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe,
+    code = E0133
+)]
 #[note]
 pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
     #[label]
@@ -112,7 +115,10 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe)]
+#[diag(
+    mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe,
+    code = E0133,
+)]
 pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
     #[label]
     pub span: Span,
@@ -121,7 +127,7 @@
 }
 
 #[derive(LintDiagnostic)]
-#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe, code = E0133)]
 #[help]
 pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
     #[label]
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 3d24a56..53a016f 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -165,9 +165,7 @@
                     }
                 }
             }
-            Rvalue::BinaryOp(overflowing_op, box (left, right))
-                if let Some(op) = overflowing_op.overflowing_to_wrapping() =>
-            {
+            Rvalue::BinaryOp(op, box (left, right)) if op.is_overflowing() => {
                 // Flood everything now, so we can use `insert_value_idx` directly later.
                 state.flood(target.as_ref(), self.map());
 
@@ -177,7 +175,7 @@
                 let overflow_target = self.map().apply(target, TrackElem::Field(1_u32.into()));
 
                 if value_target.is_some() || overflow_target.is_some() {
-                    let (val, overflow) = self.binary_op(state, op, left, right);
+                    let (val, overflow) = self.binary_op(state, *op, left, right);
 
                     if let Some(value_target) = value_target {
                         // We have flooded `target` earlier.
@@ -186,7 +184,7 @@
                     if let Some(overflow_target) = overflow_target {
                         let overflow = match overflow {
                             FlatSet::Top => FlatSet::Top,
-                            FlatSet::Elem(overflow) => FlatSet::Elem(Scalar::from_bool(overflow)),
+                            FlatSet::Elem(overflow) => FlatSet::Elem(overflow),
                             FlatSet::Bottom => FlatSet::Bottom,
                         };
                         // We have flooded `target` earlier.
@@ -266,15 +264,16 @@
                     FlatSet::Top => FlatSet::Top,
                 }
             }
-            Rvalue::BinaryOp(op, box (left, right)) => {
+            Rvalue::BinaryOp(op, box (left, right)) if !op.is_overflowing() => {
                 // Overflows must be ignored here.
+                // The overflowing operators are handled in `handle_assign`.
                 let (val, _overflow) = self.binary_op(state, *op, left, right);
                 val
             }
             Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) {
                 FlatSet::Elem(value) => self
                     .ecx
-                    .wrapping_unary_op(*op, &value)
+                    .unary_op(*op, &value)
                     .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)),
                 FlatSet::Bottom => FlatSet::Bottom,
                 FlatSet::Top => FlatSet::Top,
@@ -439,7 +438,7 @@
         op: BinOp,
         left: &Operand<'tcx>,
         right: &Operand<'tcx>,
-    ) -> (FlatSet<Scalar>, FlatSet<bool>) {
+    ) -> (FlatSet<Scalar>, FlatSet<Scalar>) {
         let left = self.eval_operand(left, state);
         let right = self.eval_operand(right, state);
 
@@ -447,9 +446,17 @@
             (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
             // Both sides are known, do the actual computation.
             (FlatSet::Elem(left), FlatSet::Elem(right)) => {
-                match self.ecx.overflowing_binary_op(op, &left, &right) {
-                    Ok((val, overflow)) => {
-                        (FlatSet::Elem(val.to_scalar()), FlatSet::Elem(overflow))
+                match self.ecx.binary_op(op, &left, &right) {
+                    // Ideally this would return an Immediate, since it's sometimes
+                    // a pair and sometimes not. But as a hack we always return a pair
+                    // and just make the 2nd component `Bottom` when it does not exist.
+                    Ok(val) => {
+                        if matches!(val.layout.abi, Abi::ScalarPair(..)) {
+                            let (val, overflow) = val.to_scalar_pair();
+                            (FlatSet::Elem(val), FlatSet::Elem(overflow))
+                        } else {
+                            (FlatSet::Elem(val.to_scalar()), FlatSet::Bottom)
+                        }
                     }
                     _ => (FlatSet::Top, FlatSet::Top),
                 }
@@ -475,7 +482,7 @@
                         (FlatSet::Elem(arg_scalar), FlatSet::Bottom)
                     }
                     BinOp::Mul if layout.ty.is_integral() && arg_value == 0 => {
-                        (FlatSet::Elem(arg_scalar), FlatSet::Elem(false))
+                        (FlatSet::Elem(arg_scalar), FlatSet::Elem(Scalar::from_bool(false)))
                     }
                     _ => (FlatSet::Top, FlatSet::Top),
                 }
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 1f3e407..9d2e715 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -223,7 +223,7 @@
     NullaryOp(NullOp<'tcx>, Ty<'tcx>),
     UnaryOp(UnOp, VnIndex),
     BinaryOp(BinOp, VnIndex, VnIndex),
-    CheckedBinaryOp(BinOp, VnIndex, VnIndex),
+    CheckedBinaryOp(BinOp, VnIndex, VnIndex), // FIXME get rid of this, work like MIR instead
     Cast {
         kind: CastKind,
         value: VnIndex,
@@ -497,7 +497,7 @@
             UnaryOp(un_op, operand) => {
                 let operand = self.evaluated[operand].as_ref()?;
                 let operand = self.ecx.read_immediate(operand).ok()?;
-                let (val, _) = self.ecx.overflowing_unary_op(un_op, &operand).ok()?;
+                let val = self.ecx.unary_op(un_op, &operand).ok()?;
                 val.into()
             }
             BinaryOp(bin_op, lhs, rhs) => {
@@ -505,7 +505,7 @@
                 let lhs = self.ecx.read_immediate(lhs).ok()?;
                 let rhs = self.evaluated[rhs].as_ref()?;
                 let rhs = self.ecx.read_immediate(rhs).ok()?;
-                let (val, _) = self.ecx.overflowing_binary_op(bin_op, &lhs, &rhs).ok()?;
+                let val = self.ecx.binary_op(bin_op, &lhs, &rhs).ok()?;
                 val.into()
             }
             CheckedBinaryOp(bin_op, lhs, rhs) => {
@@ -513,14 +513,11 @@
                 let lhs = self.ecx.read_immediate(lhs).ok()?;
                 let rhs = self.evaluated[rhs].as_ref()?;
                 let rhs = self.ecx.read_immediate(rhs).ok()?;
-                let (val, overflowed) = self.ecx.overflowing_binary_op(bin_op, &lhs, &rhs).ok()?;
-                let tuple = Ty::new_tup_from_iter(
-                    self.tcx,
-                    [val.layout.ty, self.tcx.types.bool].into_iter(),
-                );
-                let tuple = self.ecx.layout_of(tuple).ok()?;
-                ImmTy::from_scalar_pair(val.to_scalar(), Scalar::from_bool(overflowed), tuple)
-                    .into()
+                let val = self
+                    .ecx
+                    .binary_op(bin_op.wrapping_to_overflowing().unwrap(), &lhs, &rhs)
+                    .ok()?;
+                val.into()
             }
             Cast { kind, value, from: _, to } => match kind {
                 CastKind::IntToInt | CastKind::IntToFloat => {
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 38fc37a..0fa5c1b 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -304,20 +304,25 @@
 
     fn check_unary_op(&mut self, op: UnOp, arg: &Operand<'tcx>, location: Location) -> Option<()> {
         let arg = self.eval_operand(arg)?;
-        if let (val, true) = self.use_ecx(|this| {
-            let val = this.ecx.read_immediate(&arg)?;
-            let (_res, overflow) = this.ecx.overflowing_unary_op(op, &val)?;
-            Ok((val, overflow))
-        })? {
-            // `AssertKind` only has an `OverflowNeg` variant, so make sure that is
-            // appropriate to use.
-            assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow");
-            self.report_assert_as_lint(
-                location,
-                AssertLintKind::ArithmeticOverflow,
-                AssertKind::OverflowNeg(val.to_const_int()),
-            );
-            return None;
+        // The only operator that can overflow is `Neg`.
+        if op == UnOp::Neg && arg.layout.ty.is_integral() {
+            // Compute this as `0 - arg` so we can use `SubWithOverflow` to check for overflow.
+            let (arg, overflow) = self.use_ecx(|this| {
+                let arg = this.ecx.read_immediate(&arg)?;
+                let (_res, overflow) = this
+                    .ecx
+                    .binary_op(BinOp::SubWithOverflow, &ImmTy::from_int(0, arg.layout), &arg)?
+                    .to_scalar_pair();
+                Ok((arg, overflow.to_bool()?))
+            })?;
+            if overflow {
+                self.report_assert_as_lint(
+                    location,
+                    AssertLintKind::ArithmeticOverflow,
+                    AssertKind::OverflowNeg(arg.to_const_int()),
+                );
+                return None;
+            }
         }
 
         Some(())
@@ -363,11 +368,20 @@
             }
         }
 
-        if let (Some(l), Some(r)) = (l, r) {
-            // The remaining operators are handled through `overflowing_binary_op`.
+        // Div/Rem are handled via the assertions they trigger.
+        // But for Add/Sub/Mul, those assertions only exist in debug builds, and we want to
+        // lint in release builds as well, so we check on the operation instead.
+        // So normalize to the "overflowing" operator, and then ensure that it
+        // actually is an overflowing operator.
+        let op = op.wrapping_to_overflowing().unwrap_or(op);
+        // The remaining operators are handled through `wrapping_to_overflowing`.
+        if let (Some(l), Some(r)) = (l, r)
+            && l.layout.ty.is_integral()
+            && op.is_overflowing()
+        {
             if self.use_ecx(|this| {
-                let (_res, overflow) = this.ecx.overflowing_binary_op(op, &l, &r)?;
-                Ok(overflow)
+                let (_res, overflow) = this.ecx.binary_op(op, &l, &r)?.to_scalar_pair();
+                overflow.to_bool()
             })? {
                 self.report_assert_as_lint(
                     location,
@@ -399,8 +413,7 @@
             }
             Rvalue::BinaryOp(op, box (left, right)) => {
                 trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
-                let op = op.overflowing_to_wrapping().unwrap_or(*op);
-                self.check_binary_op(op, left, right, location)?;
+                self.check_binary_op(*op, left, right, location)?;
             }
 
             // Do not try creating references (#67862)
@@ -547,17 +560,15 @@
                 let right = self.eval_operand(right)?;
                 let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?;
 
-                if let Some(bin_op) = bin_op.overflowing_to_wrapping() {
-                    let (val, overflowed) =
-                        self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?;
-                    let overflowed = ImmTy::from_bool(overflowed, self.tcx);
+                let val = self.use_ecx(|this| this.ecx.binary_op(bin_op, &left, &right))?;
+                if matches!(val.layout.abi, Abi::ScalarPair(..)) {
+                    // FIXME `Value` should properly support pairs in `Immediate`... but currently it does not.
+                    let (val, overflow) = val.to_pair(&self.ecx);
                     Value::Aggregate {
                         variant: VariantIdx::ZERO,
-                        fields: [Value::from(val), overflowed.into()].into_iter().collect(),
+                        fields: [val.into(), overflow.into()].into_iter().collect(),
                     }
                 } else {
-                    let val =
-                        self.use_ecx(|this| this.ecx.wrapping_binary_op(bin_op, &left, &right))?;
                     val.into()
                 }
             }
@@ -566,7 +577,7 @@
                 let operand = self.eval_operand(operand)?;
                 let val = self.use_ecx(|this| this.ecx.read_immediate(&operand))?;
 
-                let val = self.use_ecx(|this| this.ecx.wrapping_unary_op(un_op, &val))?;
+                let val = self.use_ecx(|this| this.ecx.unary_op(un_op, &val))?;
                 val.into()
             }
 
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 7424fbe..be539d1 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -15,7 +15,7 @@
 use rustc_ast as ast;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, TokenKind};
-use rustc_ast::util::classify;
+use rustc_ast::util::classify::{self, TrailingBrace};
 use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
 use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt};
 use rustc_ast::{StmtKind, DUMMY_NODE_ID};
@@ -407,18 +407,24 @@
 
     fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
         if let Some(trailing) = classify::expr_trailing_brace(init) {
-            let sugg = match &trailing.kind {
-                ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs {
-                    left: mac.args.dspan.open,
-                    right: mac.args.dspan.close,
-                },
-                _ => errors::WrapInParentheses::Expression {
-                    left: trailing.span.shrink_to_lo(),
-                    right: trailing.span.shrink_to_hi(),
-                },
+            let (span, sugg) = match trailing {
+                TrailingBrace::MacCall(mac) => (
+                    mac.span(),
+                    errors::WrapInParentheses::MacroArgs {
+                        left: mac.args.dspan.open,
+                        right: mac.args.dspan.close,
+                    },
+                ),
+                TrailingBrace::Expr(expr) => (
+                    expr.span,
+                    errors::WrapInParentheses::Expression {
+                        left: expr.span.shrink_to_lo(),
+                        right: expr.span.shrink_to_hi(),
+                    },
+                ),
             };
             self.dcx().emit_err(errors::InvalidCurlyInLetElse {
-                span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
+                span: span.with_lo(span.hi() - BytePos(1)),
                 sugg,
             });
         }
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index 906ecdf..78653e5 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -82,7 +82,7 @@
     let mut items = DiagnosticItems::default();
 
     // Collect diagnostic items in other crates.
-    for &cnum in tcx.crates(()).iter().chain(std::iter::once(&LOCAL_CRATE)) {
+    for &cnum in tcx.crates_including_speculative(()).iter().chain(std::iter::once(&LOCAL_CRATE)) {
         for (&name, &def_id) in &tcx.diagnostic_items(cnum).name_to_id {
             collect_item(tcx, &mut items, name, def_id);
         }
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 1805527..7fdd992 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1100,7 +1100,7 @@
 pub struct OutsideLoop<'a> {
     #[primary_span]
     #[label]
-    pub span: Span,
+    pub spans: Vec<Span>,
     pub name: &'a str,
     pub is_break: bool,
     #[subdiagnostic]
@@ -1112,7 +1112,7 @@
     #[suggestion_part(code = "'block: ")]
     pub block_span: Span,
     #[suggestion_part(code = " 'block")]
-    pub break_span: Span,
+    pub break_spans: Vec<Span>,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 3b20112..2587a18 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -1,3 +1,5 @@
+use std::collections::BTreeMap;
+use std::fmt;
 use Context::*;
 
 use rustc_hir as hir;
@@ -25,22 +27,55 @@
     Closure(Span),
     Coroutine { coroutine_span: Span, kind: hir::CoroutineDesugaring, source: hir::CoroutineSource },
     UnlabeledBlock(Span),
+    UnlabeledIfBlock(Span),
     LabeledBlock,
     Constant,
 }
 
-#[derive(Copy, Clone)]
+#[derive(Clone)]
+struct BlockInfo {
+    name: String,
+    spans: Vec<Span>,
+    suggs: Vec<Span>,
+}
+
+#[derive(PartialEq)]
+enum BreakContextKind {
+    Break,
+    Continue,
+}
+
+impl fmt::Display for BreakContextKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            BreakContextKind::Break => "break",
+            BreakContextKind::Continue => "continue",
+        }
+        .fmt(f)
+    }
+}
+
+#[derive(Clone)]
 struct CheckLoopVisitor<'a, 'tcx> {
     sess: &'a Session,
     tcx: TyCtxt<'tcx>,
-    cx: Context,
+    // Keep track of a stack of contexts, so that suggestions
+    // are not made for contexts where it would be incorrect,
+    // such as adding a label for an `if`.
+    // e.g. `if 'foo: {}` would be incorrect.
+    cx_stack: Vec<Context>,
+    block_breaks: BTreeMap<Span, BlockInfo>,
 }
 
 fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
-    tcx.hir().visit_item_likes_in_module(
-        module_def_id,
-        &mut CheckLoopVisitor { sess: tcx.sess, tcx, cx: Normal },
-    );
+    let mut check = CheckLoopVisitor {
+        sess: tcx.sess,
+        tcx,
+        cx_stack: vec![Normal],
+        block_breaks: Default::default(),
+    };
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut check);
+    check.report_outside_loop_error();
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
@@ -83,6 +118,45 @@
 
     fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
         match e.kind {
+            hir::ExprKind::If(cond, then, else_opt) => {
+                self.visit_expr(cond);
+
+                let get_block = |ck_loop: &CheckLoopVisitor<'a, 'hir>,
+                                 expr: &hir::Expr<'hir>|
+                 -> Option<&hir::Block<'hir>> {
+                    if let hir::ExprKind::Block(b, None) = expr.kind
+                        && matches!(
+                            ck_loop.cx_stack.last(),
+                            Some(&Normal)
+                                | Some(&Constant)
+                                | Some(&UnlabeledBlock(_))
+                                | Some(&UnlabeledIfBlock(_))
+                        )
+                    {
+                        Some(b)
+                    } else {
+                        None
+                    }
+                };
+
+                if let Some(b) = get_block(self, then) {
+                    self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| {
+                        v.visit_block(b)
+                    });
+                } else {
+                    self.visit_expr(then);
+                }
+
+                if let Some(else_expr) = else_opt {
+                    if let Some(b) = get_block(self, else_expr) {
+                        self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| {
+                            v.visit_block(b)
+                        });
+                    } else {
+                        self.visit_expr(else_expr);
+                    }
+                }
+            }
             hir::ExprKind::Loop(ref b, _, source, _) => {
                 self.with_context(Loop(source), |v| v.visit_block(b));
             }
@@ -101,11 +175,14 @@
             hir::ExprKind::Block(ref b, Some(_label)) => {
                 self.with_context(LabeledBlock, |v| v.visit_block(b));
             }
-            hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
+            hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last(), Some(&Fn)) => {
                 self.with_context(Normal, |v| v.visit_block(b));
             }
             hir::ExprKind::Block(ref b, None)
-                if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
+                if matches!(
+                    self.cx_stack.last(),
+                    Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_))
+                ) =>
             {
                 self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
             }
@@ -178,7 +255,12 @@
                     Some(label) => sp_lo.with_hi(label.ident.span.hi()),
                     None => sp_lo.shrink_to_lo(),
                 };
-                self.require_break_cx("break", e.span, label_sp);
+                self.require_break_cx(
+                    BreakContextKind::Break,
+                    e.span,
+                    label_sp,
+                    self.cx_stack.len() - 1,
+                );
             }
             hir::ExprKind::Continue(destination) => {
                 self.require_label_in_labeled_block(e.span, &destination, "continue");
@@ -200,7 +282,12 @@
                     }
                     Err(_) => {}
                 }
-                self.require_break_cx("continue", e.span, e.span)
+                self.require_break_cx(
+                    BreakContextKind::Continue,
+                    e.span,
+                    e.span,
+                    self.cx_stack.len() - 1,
+                )
             }
             _ => intravisit::walk_expr(self, e),
         }
@@ -212,18 +299,26 @@
     where
         F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>),
     {
-        let old_cx = self.cx;
-        self.cx = cx;
+        self.cx_stack.push(cx);
         f(self);
-        self.cx = old_cx;
+        self.cx_stack.pop();
     }
 
-    fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
-        let is_break = name == "break";
-        match self.cx {
+    fn require_break_cx(
+        &mut self,
+        br_cx_kind: BreakContextKind,
+        span: Span,
+        break_span: Span,
+        cx_pos: usize,
+    ) {
+        match self.cx_stack[cx_pos] {
             LabeledBlock | Loop(_) => {}
             Closure(closure_span) => {
-                self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name });
+                self.sess.dcx().emit_err(BreakInsideClosure {
+                    span,
+                    closure_span,
+                    name: &br_cx_kind.to_string(),
+                });
             }
             Coroutine { coroutine_span, kind, source } => {
                 let kind = match kind {
@@ -239,17 +334,32 @@
                 self.sess.dcx().emit_err(BreakInsideCoroutine {
                     span,
                     coroutine_span,
-                    name,
+                    name: &br_cx_kind.to_string(),
                     kind,
                     source,
                 });
             }
-            UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
-                let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
-                self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion });
+            UnlabeledBlock(block_span)
+                if br_cx_kind == BreakContextKind::Break && block_span.eq_ctxt(break_span) =>
+            {
+                let block = self.block_breaks.entry(block_span).or_insert_with(|| BlockInfo {
+                    name: br_cx_kind.to_string(),
+                    spans: vec![],
+                    suggs: vec![],
+                });
+                block.spans.push(span);
+                block.suggs.push(break_span);
             }
-            Normal | Constant | Fn | UnlabeledBlock(_) => {
-                self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None });
+            UnlabeledIfBlock(_) if br_cx_kind == BreakContextKind::Break => {
+                self.require_break_cx(br_cx_kind, span, break_span, cx_pos - 1);
+            }
+            Normal | Constant | Fn | UnlabeledBlock(_) | UnlabeledIfBlock(_) => {
+                self.sess.dcx().emit_err(OutsideLoop {
+                    spans: vec![span],
+                    name: &br_cx_kind.to_string(),
+                    is_break: br_cx_kind == BreakContextKind::Break,
+                    suggestion: None,
+                });
             }
         }
     }
@@ -261,7 +371,7 @@
         cf_type: &str,
     ) -> bool {
         if !span.is_desugaring(DesugaringKind::QuestionMark)
-            && self.cx == LabeledBlock
+            && self.cx_stack.last() == Some(&LabeledBlock)
             && label.label.is_none()
         {
             self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
@@ -269,4 +379,18 @@
         }
         false
     }
+
+    fn report_outside_loop_error(&mut self) {
+        for (s, block) in &self.block_breaks {
+            self.sess.dcx().emit_err(OutsideLoop {
+                spans: block.spans.clone(),
+                name: &block.name,
+                is_break: true,
+                suggestion: Some(OutsideLoopSuggestion {
+                    block_span: *s,
+                    break_spans: block.suggs.clone(),
+                }),
+            });
+        }
+    }
 }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 05c833c..4ed0e6a 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -1020,7 +1020,7 @@
         // stabilization diagnostic, but it can be avoided when there are no
         // `remaining_lib_features`.
         let mut all_implications = remaining_implications.clone();
-        for &cnum in tcx.crates(()) {
+        for &cnum in tcx.used_crates(()) {
             all_implications
                 .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
         }
@@ -1033,7 +1033,7 @@
             &all_implications,
         );
 
-        for &cnum in tcx.crates(()) {
+        for &cnum in tcx.used_crates(()) {
             if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
                 break;
             }
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index 90691ca..d80addf 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -68,7 +68,7 @@
     }
 
     let mut missing = FxHashSet::default();
-    for &cnum in tcx.crates(()).iter() {
+    for &cnum in tcx.used_crates(()).iter() {
         for &item in tcx.missing_lang_items(cnum).iter() {
             missing.insert(item);
         }
diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs
index b426bb8..8e91d9d 100644
--- a/compiler/rustc_query_system/src/dep_graph/serialized.rs
+++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs
@@ -182,15 +182,13 @@
     pub fn decode<D: Deps>(d: &mut MemDecoder<'_>) -> Arc<SerializedDepGraph> {
         // The last 16 bytes are the node count and edge count.
         debug!("position: {:?}", d.position());
-        let (node_count, edge_count, graph_size) =
-            d.with_position(d.len() - 3 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| {
+        let (node_count, edge_count) =
+            d.with_position(d.len() - 2 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| {
                 debug!("position: {:?}", d.position());
                 let node_count = IntEncodedWithFixedSize::decode(d).0 as usize;
                 let edge_count = IntEncodedWithFixedSize::decode(d).0 as usize;
-                let graph_size = IntEncodedWithFixedSize::decode(d).0 as usize;
-                (node_count, edge_count, graph_size)
+                (node_count, edge_count)
             });
-        assert_eq!(d.len(), graph_size);
         debug!("position: {:?}", d.position());
 
         debug!(?node_count, ?edge_count);
@@ -606,8 +604,6 @@
         debug!("position: {:?}", encoder.position());
         IntEncodedWithFixedSize(node_count).encode(&mut encoder);
         IntEncodedWithFixedSize(edge_count).encode(&mut encoder);
-        let graph_size = encoder.position() + IntEncodedWithFixedSize::ENCODED_SIZE;
-        IntEncodedWithFixedSize(graph_size as u64).encode(&mut encoder);
         debug!("position: {:?}", encoder.position());
         // Drop the encoder so that nothing is written after the counts.
         let result = encoder.finish();
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index d1d0e33..63d0d6c 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -12,7 +12,7 @@
 use crate::{ResolutionError, Resolver, Segment, UseError};
 
 use rustc_ast::ptr::P;
-use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
+use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
 use rustc_ast::*;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_errors::{codes::*, Applicability, DiagArgValue, IntoDiagArg, StashKey};
@@ -3280,7 +3280,7 @@
     fn resolve_local(&mut self, local: &'ast Local) {
         debug!("resolving local ({:?})", local);
         // Resolve the type.
-        walk_list!(self, visit_ty, &local.ty);
+        visit_opt!(self, visit_ty, &local.ty);
 
         // Resolve the initializer.
         if let Some((init, els)) = local.kind.init_else_opt() {
@@ -3479,8 +3479,8 @@
     fn resolve_arm(&mut self, arm: &'ast Arm) {
         self.with_rib(ValueNS, RibKind::Normal, |this| {
             this.resolve_pattern_top(&arm.pat, PatternSource::Match);
-            walk_list!(this, visit_expr, &arm.guard);
-            walk_list!(this, visit_expr, &arm.body);
+            visit_opt!(this, visit_expr, &arm.guard);
+            visit_opt!(this, visit_expr, &arm.body);
         });
     }
 
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index eec83c02..1dcb699 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -17,6 +17,8 @@
 
 pub type FileEncodeResult = Result<usize, (PathBuf, io::Error)>;
 
+pub const MAGIC_END_BYTES: &[u8] = b"rust-end-file";
+
 /// The size of the buffer in `FileEncoder`.
 const BUF_SIZE: usize = 8192;
 
@@ -181,6 +183,7 @@
     }
 
     pub fn finish(&mut self) -> FileEncodeResult {
+        self.write_all(MAGIC_END_BYTES);
         self.flush();
         #[cfg(debug_assertions)]
         {
@@ -261,15 +264,18 @@
 
 impl<'a> MemDecoder<'a> {
     #[inline]
-    pub fn new(data: &'a [u8], position: usize) -> MemDecoder<'a> {
+    pub fn new(data: &'a [u8], position: usize) -> Result<MemDecoder<'a>, ()> {
+        let data = data.strip_suffix(MAGIC_END_BYTES).ok_or(())?;
         let Range { start, end } = data.as_ptr_range();
-        MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData }
+        Ok(MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData })
     }
 
     #[inline]
-    pub fn data(&self) -> &'a [u8] {
-        // SAFETY: This recovers the original slice, only using members we never modify.
-        unsafe { std::slice::from_raw_parts(self.start, self.len()) }
+    pub fn split_at(&self, position: usize) -> MemDecoder<'a> {
+        assert!(position <= self.len());
+        // SAFETY: We checked above that this offset is within the original slice
+        let current = unsafe { self.start.add(position) };
+        MemDecoder { start: self.start, current, end: self.end, _marker: PhantomData }
     }
 
     #[inline]
diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs
index dc9b32a..fafe4b9 100644
--- a/compiler/rustc_serialize/tests/leb128.rs
+++ b/compiler/rustc_serialize/tests/leb128.rs
@@ -1,4 +1,6 @@
 use rustc_serialize::leb128::*;
+use rustc_serialize::opaque::MemDecoder;
+use rustc_serialize::opaque::MAGIC_END_BYTES;
 use rustc_serialize::Decoder;
 
 macro_rules! impl_test_unsigned_leb128 {
@@ -25,13 +27,15 @@
                 let n = $write_fn_name(&mut buf, x);
                 stream.extend(&buf[..n]);
             }
+            let stream_end = stream.len();
+            stream.extend(MAGIC_END_BYTES);
 
-            let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0);
+            let mut decoder = MemDecoder::new(&stream, 0).unwrap();
             for &expected in &values {
                 let actual = $read_fn_name(&mut decoder);
                 assert_eq!(expected, actual);
             }
-            assert_eq!(stream.len(), decoder.position());
+            assert_eq!(stream_end, decoder.position());
         }
     };
 }
@@ -72,13 +76,15 @@
                 let n = $write_fn_name(&mut buf, x);
                 stream.extend(&buf[..n]);
             }
+            let stream_end = stream.len();
+            stream.extend(MAGIC_END_BYTES);
 
-            let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0);
+            let mut decoder = MemDecoder::new(&stream, 0).unwrap();
             for &expected in &values {
                 let actual = $read_fn_name(&mut decoder);
                 assert_eq!(expected, actual);
             }
-            assert_eq!(stream.len(), decoder.position());
+            assert_eq!(stream_end, decoder.position());
         }
     };
 }
diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs
index 45ff85f..833151d 100644
--- a/compiler/rustc_serialize/tests/opaque.rs
+++ b/compiler/rustc_serialize/tests/opaque.rs
@@ -42,7 +42,7 @@
     encoder.finish().unwrap();
 
     let data = fs::read(&tmpfile).unwrap();
-    let mut decoder = MemDecoder::new(&data[..], 0);
+    let mut decoder = MemDecoder::new(&data[..], 0).unwrap();
     for value in values {
         let decoded = Decodable::decode(&mut decoder);
         assert_eq!(value, decoded);
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index fd31c02..242a48b 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -126,7 +126,7 @@
         let mut tables = self.0.borrow_mut();
         let tcx = tables.tcx;
         iter::once(LOCAL_CRATE)
-            .chain(tables.tcx.crates(()).iter().copied())
+            .chain(tables.tcx.used_crates(()).iter().copied())
             .flat_map(|cnum| tcx.trait_impls_in_crate(cnum).iter())
             .map(|impl_def_id| tables.impl_def(*impl_def_id))
             .collect()
@@ -201,14 +201,19 @@
 
     fn external_crates(&self) -> Vec<stable_mir::Crate> {
         let tables = self.0.borrow();
-        tables.tcx.crates(()).iter().map(|crate_num| smir_crate(tables.tcx, *crate_num)).collect()
+        tables
+            .tcx
+            .used_crates(())
+            .iter()
+            .map(|crate_num| smir_crate(tables.tcx, *crate_num))
+            .collect()
     }
 
     fn find_crates(&self, name: &str) -> Vec<stable_mir::Crate> {
         let tables = self.0.borrow();
         let crates: Vec<stable_mir::Crate> = [LOCAL_CRATE]
             .iter()
-            .chain(tables.tcx.crates(()).iter())
+            .chain(tables.tcx.used_crates(()).iter())
             .filter_map(|crate_num| {
                 let crate_name = tables.tcx.crate_name(*crate_num).to_string();
                 (name == crate_name).then(|| smir_crate(tables.tcx, *crate_num))
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index aa4bcef..00ef17d 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -40,6 +40,7 @@
 use std::collections::hash_map::Entry;
 use std::fmt;
 use std::hash::Hash;
+use tracing::{debug, trace};
 
 /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks".
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index f83bacd..b2ca01f 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -33,15 +33,16 @@
 #![feature(rustdoc_internals)]
 // tidy-alphabetical-end
 
+// The code produced by the `Encodable`/`Decodable` derive macros refer to
+// `rustc_span::Span{Encoder,Decoder}`. That's fine outside this crate, but doesn't work inside
+// this crate without this line making `rustc_span` available.
 extern crate self as rustc_span;
 
-#[macro_use]
-extern crate tracing;
-
 use rustc_data_structures::{outline, AtomicRef};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_serialize::opaque::{FileEncoder, MemDecoder};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use tracing::debug;
 
 mod caching_source_map_view;
 pub mod source_map;
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 2093dcf..fb212d6 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -16,6 +16,7 @@
 use std::fs;
 use std::io::{self, BorrowedBuf, Read};
 use std::path;
+use tracing::{debug, instrument, trace};
 
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 930ae5a..6f68875 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -300,14 +300,11 @@
                     return Err(NoSolution);
                 }
 
-                // If `Fn`/`FnMut`, we only implement this goal if we
-                // have no captures.
-                let no_borrows = match args.tupled_upvars_ty().kind() {
-                    ty::Tuple(tys) => tys.is_empty(),
-                    ty::Error(_) => false,
-                    _ => bug!("tuple_fields called on non-tuple"),
-                };
-                if closure_kind != ty::ClosureKind::FnOnce && !no_borrows {
+                // A coroutine-closure implements `FnOnce` *always*, since it may
+                // always be called once. It additionally implements `Fn`/`FnMut`
+                // only if it has no upvars referencing the closure-env lifetime,
+                // and if the closure kind permits it.
+                if closure_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() {
                     return Err(NoSolution);
                 }
 
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 b9e853a..fd7c47a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -418,20 +418,11 @@
                     // Ambiguity if upvars haven't been constrained yet
                     && !args.tupled_upvars_ty().is_ty_var()
                 {
-                    let no_borrows = match args.tupled_upvars_ty().kind() {
-                        ty::Tuple(tys) => tys.is_empty(),
-                        ty::Error(_) => false,
-                        _ => bug!("tuple_fields called on non-tuple"),
-                    };
                     // A coroutine-closure implements `FnOnce` *always*, since it may
                     // always be called once. It additionally implements `Fn`/`FnMut`
-                    // only if it has no upvars (therefore no borrows from the closure
-                    // that would need to be represented with a lifetime) and if the
-                    // closure kind permits it.
-                    // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
-                    // if it takes all of its upvars by copy, and none by ref. This would
-                    // require us to record a bit more information during upvar analysis.
-                    if no_borrows && closure_kind.extends(kind) {
+                    // only if it has no upvars referencing the closure-env lifetime,
+                    // and if the closure kind permits it.
+                    if closure_kind.extends(kind) && !args.has_self_borrows() {
                         candidates.vec.push(ClosureCandidate { is_const });
                     } else if kind == ty::ClosureKind::FnOnce {
                         candidates.vec.push(ClosureCandidate { is_const });
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
index 2e7fcb9..3960f71 100644
--- a/library/alloc/Cargo.toml
+++ b/library/alloc/Cargo.toml
@@ -40,3 +40,15 @@
 panic_immediate_abort = ["core/panic_immediate_abort"]
 # Choose algorithms that are optimized for binary size instead of runtime performance
 optimize_for_size = ["core/optimize_for_size"]
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+# x.py uses beta cargo, so `check-cfg` entries do not yet take effect
+# for rust-lang/rust. But for users of `-Zbuild-std` it does.
+# The unused warning is waiting for rust-lang/cargo#13925 to reach beta.
+check-cfg = [
+    'cfg(bootstrap)',
+    'cfg(no_global_oom_handling)',
+    'cfg(no_rc)',
+    'cfg(no_sync)',
+]
diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml
index 11d3397..daf2612 100644
--- a/library/core/Cargo.toml
+++ b/library/core/Cargo.toml
@@ -36,3 +36,16 @@
 # Make `RefCell` store additional debugging information, which is printed out when
 # a borrow error occurs
 debug_refcell = []
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+# x.py uses beta cargo, so `check-cfg` entries do not yet take effect
+# for rust-lang/rust. But for users of `-Zbuild-std` it does.
+# The unused warning is waiting for rust-lang/cargo#13925 to reach beta.
+check-cfg = [
+    'cfg(bootstrap)',
+    'cfg(no_fp_fmt_parse)',
+    'cfg(stdarch_intel_sde)',
+    # This matches `EXTRA_CHECK_CFGS` in `src/bootstrap/src/lib.rs`.
+    'cfg(feature, values(any()))',
+]
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index d58e1db..5a2a4c5 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -987,7 +987,7 @@
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[rustc_intrinsic]
 #[rustc_nounwind]
-#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
+#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
 pub const fn likely(b: bool) -> bool {
     b
 }
@@ -1007,7 +1007,7 @@
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[rustc_intrinsic]
 #[rustc_nounwind]
-#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
+#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
 pub const fn unlikely(b: bool) -> bool {
     b
 }
@@ -1483,10 +1483,10 @@
     ///
     /// # Safety
     ///
-    /// Both the starting and resulting pointer must be either in bounds or one
-    /// byte past the end of an allocated object. If either pointer is out of
-    /// bounds or arithmetic overflow occurs then any further use of the
-    /// returned value will result in undefined behavior.
+    /// If the computed offset is non-zero, then both the starting and resulting pointer must be
+    /// either in bounds or at the end of an allocated object. If either pointer is out
+    /// of bounds or arithmetic overflow occurs then any further use of the returned value will
+    /// result in undefined behavior.
     ///
     /// The stabilized version of this intrinsic is [`pointer::offset`].
     #[must_use = "returns a new pointer rather than modifying its argument"]
@@ -1502,7 +1502,7 @@
     /// # Safety
     ///
     /// Unlike the `offset` intrinsic, this intrinsic does not restrict the
-    /// resulting pointer to point into or one byte past the end of an allocated
+    /// resulting pointer to point into or at the end of an allocated
     /// object, and it wraps with two's complement arithmetic. The resulting
     /// value is not necessarily valid to be used to actually access memory.
     ///
@@ -2483,7 +2483,7 @@
 #[rustc_nounwind]
 #[rustc_do_not_const_check]
 #[inline]
-#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
+#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
 pub const fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8 {
     (ptr == other) as u8
 }
@@ -2748,7 +2748,7 @@
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[rustc_nounwind]
 #[rustc_intrinsic]
-#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
+#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
 pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 {
     // const eval overrides this function, but runtime code for now just returns null pointers.
     // See <https://github.com/rust-lang/rust/issues/93935>.
@@ -2769,7 +2769,7 @@
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[rustc_nounwind]
 #[rustc_intrinsic]
-#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)]
+#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
 pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {
     // Runtime NOP
 }
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 8283fdc..5989bcb 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1476,14 +1476,17 @@
 ///
 /// For instance, this means that unsafe code in a safe function may assume these invariants are
 /// ensured of arguments passed by the caller, and it may assume that these invariants are ensured
-/// of return values from any safe functions it calls. In most cases, the inverse is also true:
-/// unsafe code must not violate these invariants when passing arguments to safe functions or
-/// returning values from safe functions; such violations may result in undefined behavior. Where
-/// exceptions to this latter requirement exist, they will be called out explicitly in documentation.
+/// of return values from any safe functions it calls.
+///
+/// For the other direction, things are more complicated: when unsafe code passes arguments
+/// to safe functions or returns values from safe functions, they generally must *at least*
+/// not violate these invariants. The full requirements are stronger, as the reference generally
+/// must point to data that is safe to use at type `T`.
 ///
 /// It is not decided yet whether unsafe code may violate these invariants temporarily on internal
 /// data. As a consequence, unsafe code which violates these invariants temporarily on internal data
-/// may become unsound in future versions of Rust depending on how this question is decided.
+/// may be unsound or become unsound in future versions of Rust depending on how this question is
+/// decided.
 ///
 /// [allocated object]: ptr#allocated-object
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 73bb256..c8065b2 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -465,8 +465,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -676,11 +677,11 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both `self` and `origin` must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * `self` and `origin` must either
     ///
-    /// * Both pointers must be *derived from* a pointer to the same object.
-    ///   (See below for an example.)
+    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///     the two pointers must be either empty or in bounds of that object. (See below for an example.)
+    ///   * or both be derived from an integer literal/constant, and point to the same address.
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
     ///   of the size of `T`.
@@ -951,8 +952,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -1035,8 +1037,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset cannot exceed `isize::MAX` **bytes**.
     ///
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index f87b6bb..d2bbdc8 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -15,18 +15,13 @@
 //! The precise rules for validity are not determined yet. The guarantees that are
 //! provided at this point are very minimal:
 //!
-//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst].
+//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer.
+//!   The following points are only concerned with non-zero-sized accesses.
+//! * A [null] pointer is *never* valid.
 //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer
 //!   be *dereferenceable*: the memory range of the given size starting at the pointer must all be
 //!   within the bounds of a single allocated object. Note that in Rust,
 //!   every (stack-allocated) variable is considered a separate allocated object.
-//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated
-//!   memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However,
-//!   casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if
-//!   some memory happens to exist at that address and gets deallocated. This corresponds to writing
-//!   your own allocator: allocating zero-sized objects is not very hard. The canonical way to
-//!   obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`].
-//FIXME: mention `ptr::dangling` above, once it is stable.
 //! * All accesses performed by functions in this module are *non-atomic* in the sense
 //!   of [atomic operations] used to synchronize between threads. This means it is
 //!   undefined behavior to perform two concurrent accesses to the same location from different
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 7856a1d..c539534 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -480,8 +480,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -904,11 +905,11 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both `self` and `origin` must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * `self` and `origin` must either
     ///
-    /// * Both pointers must be *derived from* a pointer to the same object.
-    ///   (See below for an example.)
+    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///     the two pointers must be either empty or in bounds of that object. (See below for an example.)
+    ///   * or both be derived from an integer literal/constant, and point to the same address.
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
     ///   of the size of `T`.
@@ -1095,8 +1096,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -1179,8 +1181,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset cannot exceed `isize::MAX` **bytes**.
     ///
diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs
index 7691721..3d21b09 100644
--- a/library/core/src/task/wake.rs
+++ b/library/core/src/task/wake.rs
@@ -5,6 +5,7 @@
 use crate::any::Any;
 use crate::fmt;
 use crate::marker::PhantomData;
+use crate::panic::AssertUnwindSafe;
 use crate::ptr;
 
 /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`]
@@ -236,7 +237,7 @@
 pub struct Context<'a> {
     waker: &'a Waker,
     local_waker: &'a LocalWaker,
-    ext: ExtData<'a>,
+    ext: AssertUnwindSafe<ExtData<'a>>,
     // Ensure we future-proof against variance changes by forcing
     // the lifetime to be invariant (argument-position lifetimes
     // are contravariant while return-position lifetimes are
@@ -279,7 +280,9 @@
     #[unstable(feature = "context_ext", issue = "123392")]
     #[rustc_const_unstable(feature = "const_waker", issue = "102012")]
     pub const fn ext(&mut self) -> &mut dyn Any {
-        match &mut self.ext {
+        // FIXME: this field makes Context extra-weird about unwind safety
+        // can we justify AssertUnwindSafe if we stabilize this? do we care?
+        match &mut *self.ext {
             ExtData::Some(data) => *data,
             ExtData::None(unit) => unit,
         }
@@ -353,7 +356,7 @@
     #[rustc_const_unstable(feature = "const_waker", issue = "102012")]
     #[unstable(feature = "context_ext", issue = "123392")]
     pub const fn from(cx: &'a mut Context<'_>) -> Self {
-        let ext = match &mut cx.ext {
+        let ext = match &mut *cx.ext {
             ExtData::Some(ext) => ExtData::Some(*ext),
             ExtData::None(()) => ExtData::None(()),
         };
@@ -396,7 +399,7 @@
     #[rustc_const_unstable(feature = "const_waker", issue = "102012")]
     pub const fn build(self) -> Context<'a> {
         let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self;
-        Context { waker, local_waker, ext, _marker, _marker2 }
+        Context { waker, local_waker, ext: AssertUnwindSafe(ext), _marker, _marker2 }
     }
 }
 
diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs
index a47ef7a..00a6fd7 100644
--- a/library/core/tests/result.rs
+++ b/library/core/tests/result.rs
@@ -170,6 +170,7 @@
 }
 
 #[test]
+#[allow(for_loops_over_fallibles)]
 pub fn test_iter_mut() {
     let mut ok: Result<isize, &'static str> = Ok(100);
     for loc in ok.iter_mut() {
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 5b36867..4b8ee4c 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -97,3 +97,13 @@
 name = "stdbenches"
 path = "benches/lib.rs"
 test = true
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = [
+    'cfg(bootstrap)',
+    'cfg(backtrace_in_libstd)',
+    'cfg(netbsd10)',
+    'cfg(target_arch, values("xtensa"))',
+    'cfg(feature, values("std", "as_crate"))',
+]
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 698a576..38de5e3 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -84,6 +84,9 @@
     (Some(Mode::ToolRustc), "rust_analyzer", None),
     (Some(Mode::ToolStd), "rust_analyzer", None),
     (Some(Mode::Codegen), "parallel_compiler", None),
+    // NOTE: consider updating `check-cfg` entries in `std/Cargo.toml` too.
+    // cfg(bootstrap) remove these once the bootstrap compiler supports
+    // `lints.rust.unexpected_cfgs.check-cfg`
     (Some(Mode::Std), "stdarch_intel_sde", None),
     (Some(Mode::Std), "no_fp_fmt_parse", None),
     (Some(Mode::Std), "no_global_oom_handling", None),
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 6f81495..4ef2fce 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -20,10 +20,10 @@
     - [\*-apple-darwin](platform-support/apple-darwin.md)
         - [i686-apple-darwin](platform-support/i686-apple-darwin.md)
         - [x86_64h-apple-darwin](platform-support/x86_64h-apple-darwin.md)
-        - [arm64e-apple-darwin.md](platform-support/arm64e-apple-darwin.md)
+        - [arm64e-apple-darwin](platform-support/arm64e-apple-darwin.md)
     - [\*-apple-ios](platform-support/apple-ios.md)
         - [\*-apple-ios-macabi](platform-support/apple-ios-macabi.md)
-        - [arm64e-apple-ios.md](platform-support/arm64e-apple-ios.md)
+        - [arm64e-apple-ios](platform-support/arm64e-apple-ios.md)
     - [\*-apple-tvos](platform-support/apple-tvos.md)
     - [\*-apple-watchos](platform-support/apple-watchos.md)
     - [\*-apple-visionos](platform-support/apple-visionos.md)
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index b54ec62..7aab07c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1937,7 +1937,7 @@
             let mut primitive_locations = FxHashMap::default();
             // NOTE: technically this misses crates that are only passed with `--extern` and not loaded when checking the crate.
             // This is a degenerate case that I don't plan to support.
-            for &crate_num in tcx.crates(()) {
+            for &crate_num in tcx.crates_including_speculative(()) {
                 let e = ExternalCrate { crate_num };
                 let crate_name = e.name(tcx);
                 debug!(?crate_num, ?crate_name);
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index feb03b9..462b31f 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -347,7 +347,7 @@
         show_coverage,
     };
 
-    for cnum in tcx.crates(()) {
+    for cnum in tcx.crates_including_speculative(()) {
         crate::visit_lib::lib_embargo_visit_item(&mut ctxt, cnum.as_def_id());
     }
 
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index a3b88a8..28ddf76 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -155,7 +155,7 @@
 
         // Cache where all our extern crates are located
         // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code
-        for &crate_num in tcx.crates(()) {
+        for &crate_num in tcx.crates_including_speculative(()) {
             let e = ExternalCrate { crate_num };
 
             let name = e.name(tcx);
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index c92cf9d..22daac9 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -47,7 +47,7 @@
     // External trait impls.
     {
         let _prof_timer = tcx.sess.prof.generic_activity("build_extern_trait_impls");
-        for &cnum in tcx.crates(()) {
+        for &cnum in tcx.crates_including_speculative(()) {
             for &impl_def_id in tcx.trait_impls_in_crate(cnum) {
                 cx.with_param_env(impl_def_id, |cx| {
                     inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 9c9b386..64753a5 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -283,7 +283,7 @@
         // Collect CrateIds corresponding to provided target crates
         // If two different versions of the crate in the dependency tree, then examples will be collected from both.
         let all_crates = tcx
-            .crates(())
+            .crates_including_speculative(())
             .iter()
             .chain([&LOCAL_CRATE])
             .map(|crate_num| (crate_num, tcx.crate_name(*crate_num)))
@@ -344,7 +344,9 @@
             Ok(bytes) => bytes,
             Err(e) => dcx.fatal(format!("failed to load examples: {e}")),
         };
-        let mut decoder = MemDecoder::new(&bytes, 0);
+        let Ok(mut decoder) = MemDecoder::new(&bytes, 0) else {
+            dcx.fatal(format!("Corrupt metadata encountered in {path}"))
+        };
         let calls = AllCallLocations::decode(&mut decoder);
 
         for (function, fn_calls) in calls.into_iter() {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 4c603bd..94d4656 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -647,7 +647,7 @@
 /// This function is expensive and should be used sparingly.
 pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> {
     fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
-        tcx.crates(())
+        tcx.crates_including_speculative(())
             .iter()
             .copied()
             .filter(move |&num| tcx.crate_name(num) == name)
diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs
index 10726b9..29e8809 100644
--- a/src/tools/compiletest/src/json.rs
+++ b/src/tools/compiletest/src/json.rs
@@ -282,7 +282,7 @@
 
     // Add notes for the backtrace
     for span in primary_spans {
-        for frame in &span.expansion {
+        if let Some(frame) = &span.expansion {
             push_backtrace(expected_errors, frame, file_name);
         }
     }
@@ -315,7 +315,7 @@
         });
     }
 
-    for previous_expansion in &expansion.span.expansion {
+    if let Some(previous_expansion) = &expansion.span.expansion {
         push_backtrace(expected_errors, previous_expansion, file_name);
     }
 }
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index f2bec97..10835e0 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -648,7 +648,7 @@
         place: &MPlaceTy<'tcx, Provenance>,
         rhs: &ImmTy<'tcx, Provenance>,
         op: mir::BinOp,
-        neg: bool,
+        not: bool,
         atomic: AtomicRwOrd,
     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
         let this = self.eval_context_mut();
@@ -656,9 +656,8 @@
 
         let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
 
-        // Atomics wrap around on overflow.
-        let val = this.wrapping_binary_op(op, &old, rhs)?;
-        let val = if neg { this.wrapping_unary_op(mir::UnOp::Not, &val)? } else { val };
+        let val = this.binary_op(op, &old, rhs)?;
+        let val = if not { this.unary_op(mir::UnOp::Not, &val)? } else { val };
         this.allow_data_races_mut(|this| this.write_immediate(*val, place))?;
 
         this.validate_atomic_rmw(place, atomic)?;
@@ -700,7 +699,7 @@
         this.atomic_access_check(place, AtomicAccessType::Rmw)?;
 
         let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
-        let lt = this.wrapping_binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
+        let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
 
         #[rustfmt::skip] // rustfmt makes this unreadable
         let new_val = if min {
@@ -744,7 +743,7 @@
         // Read as immediate for the sake of `binary_op()`
         let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
         // `binary_op` will bail if either of them is not a scalar.
-        let eq = this.wrapping_binary_op(mir::BinOp::Eq, &old, expect_old)?;
+        let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?;
         // If the operation would succeed, but is "weak", fail some portion
         // of the time, based on `success_rate`.
         let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate;
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 4050ae3..bd16038 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -126,7 +126,7 @@
     // the one in the sysroot and the one locally built by `cargo test`.)
     // FIXME: can we prefer the one from the sysroot?
     'crates: for krate in
-        tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
+        tcx.used_crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
     {
         let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
         // Go over the modules.
@@ -1365,7 +1365,7 @@
         .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
         .unwrap_or_default();
     let mut local_crates = Vec::new();
-    for &crate_num in tcx.crates(()) {
+    for &crate_num in tcx.crates_including_speculative(()) {
         let name = tcx.crate_name(crate_num);
         let name = name.as_str();
         if local_crate_names.iter().any(|local_name| local_name == name) {
diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs
index 501dbc1..0daf59c 100644
--- a/src/tools/miri/src/intrinsics/atomic.rs
+++ b/src/tools/miri/src/intrinsics/atomic.rs
@@ -4,8 +4,8 @@
 use helpers::check_arg_count;
 
 pub enum AtomicOp {
-    /// The `bool` indicates whether the result of the operation should be negated
-    /// (must be a boolean-typed operation).
+    /// The `bool` indicates whether the result of the operation should be negated (`UnOp::Not`,
+    /// must be a boolean-typed operation).
     MirOp(mir::BinOp, bool),
     Max,
     Min,
@@ -213,8 +213,8 @@
                 this.write_immediate(*old, dest)?; // old value is returned
                 Ok(())
             }
-            AtomicOp::MirOp(op, neg) => {
-                let old = this.atomic_rmw_op_immediate(&place, &rhs, op, neg, atomic)?;
+            AtomicOp::MirOp(op, not) => {
+                let old = this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?;
                 this.write_immediate(*old, dest)?; // old value is returned
                 Ok(())
             }
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 0a7927d..3e39376 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -43,18 +43,18 @@
                 if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
                     throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`")
                 }
-                let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub");
+                let intrinsic_fallback_is_spec = Symbol::intern("intrinsic_fallback_is_spec");
                 if this
                     .tcx
                     .get_attrs_by_path(
                         instance.def_id(),
-                        &[sym::miri, intrinsic_fallback_checks_ub],
+                        &[sym::miri, intrinsic_fallback_is_spec],
                     )
                     .next()
                     .is_none()
                 {
                     throw_unsup_format!(
-                        "miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that"
+                        "Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that"
                     );
                 }
                 Ok(Some(ty::Instance {
@@ -365,8 +365,8 @@
                     "frem_algebraic" => mir::BinOp::Rem,
                     _ => bug!(),
                 };
-                let res = this.wrapping_binary_op(op, &a, &b)?;
-                // `wrapping_binary_op` already called `generate_nan` if necessary.
+                let res = this.binary_op(op, &a, &b)?;
+                // `binary_op` already called `generate_nan` if necessary.
                 this.write_immediate(*res, dest)?;
             }
 
@@ -411,12 +411,12 @@
                     ),
                     _ => {}
                 }
-                let res = this.wrapping_binary_op(op, &a, &b)?;
+                let res = this.binary_op(op, &a, &b)?;
                 if !float_finite(&res)? {
                     throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
                 }
                 // This cannot be a NaN so we also don't have to apply any non-determinism.
-                // (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
+                // (Also, `binary_op` already called `generate_nan` if needed.)
                 this.write_immediate(*res, dest)?;
             }
 
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index e178187..67a2d56 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -1,3 +1,5 @@
+use either::Either;
+
 use rustc_apfloat::{Float, Round};
 use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
 use rustc_middle::{mir, ty, ty::FloatTy};
@@ -82,7 +84,7 @@
                     let val = match which {
                         Op::MirOp(mir_op) => {
                             // This already does NaN adjustments
-                            this.wrapping_unary_op(mir_op, &op)?.to_scalar()
+                            this.unary_op(mir_op, &op)?.to_scalar()
                         }
                         Op::Abs => {
                             // Works for f32 and f64.
@@ -217,8 +219,8 @@
                     "mul" => Op::MirOp(BinOp::Mul),
                     "div" => Op::MirOp(BinOp::Div),
                     "rem" => Op::MirOp(BinOp::Rem),
-                    "shl" => Op::MirOp(BinOp::Shl),
-                    "shr" => Op::MirOp(BinOp::Shr),
+                    "shl" => Op::MirOp(BinOp::ShlUnchecked),
+                    "shr" => Op::MirOp(BinOp::ShrUnchecked),
                     "and" => Op::MirOp(BinOp::BitAnd),
                     "or" => Op::MirOp(BinOp::BitOr),
                     "xor" => Op::MirOp(BinOp::BitXor),
@@ -243,15 +245,19 @@
                     let val = match which {
                         Op::MirOp(mir_op) => {
                             // This does NaN adjustments.
-                            let (val, overflowed) = this.overflowing_binary_op(mir_op, &left, &right)?;
-                            if matches!(mir_op, BinOp::Shl | BinOp::Shr) {
-                                // Shifts have extra UB as SIMD operations that the MIR binop does not have.
-                                // See <https://github.com/rust-lang/rust/issues/91237>.
-                                if overflowed {
-                                    let r_val = right.to_scalar().to_bits(right.layout.size)?;
-                                    throw_ub_format!("overflowing shift by {r_val} in `simd_{intrinsic_name}` in SIMD lane {i}");
+                            let val = this.binary_op(mir_op, &left, &right).map_err(|err| {
+                                match err.kind() {
+                                    InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => {
+                                        // This resets the interpreter backtrace, but it's not worth avoiding that.
+                                        let shift_amount = match shift_amount {
+                                            Either::Left(v) => v.to_string(),
+                                            Either::Right(v) => v.to_string(),
+                                        };
+                                        err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}").into()
+                                    }
+                                    _ => err
                                 }
-                            }
+                            })?;
                             if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
                                 // Special handling for boolean-returning operations
                                 assert_eq!(val.layout.ty, this.tcx.types.bool);
@@ -370,11 +376,11 @@
                     let op = this.read_immediate(&this.project_index(&op, i)?)?;
                     res = match which {
                         Op::MirOp(mir_op) => {
-                            this.wrapping_binary_op(mir_op, &res, &op)?
+                            this.binary_op(mir_op, &res, &op)?
                         }
                         Op::MirOpBool(mir_op) => {
                             let op = imm_from_bool(simd_element_to_bool(op)?);
-                            this.wrapping_binary_op(mir_op, &res, &op)?
+                            this.binary_op(mir_op, &res, &op)?
                         }
                         Op::MinMax(mmop) => {
                             if matches!(res.layout.ty.kind(), ty::Float(_)) {
@@ -385,7 +391,7 @@
                                     MinMax::Min => BinOp::Le,
                                     MinMax::Max => BinOp::Ge,
                                 };
-                                if this.wrapping_binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
+                                if this.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
                                     res
                                 } else {
                                     op
@@ -414,7 +420,7 @@
                 let mut res = init;
                 for i in 0..op_len {
                     let op = this.read_immediate(&this.project_index(&op, i)?)?;
-                    res = this.wrapping_binary_op(mir_op, &res, &op)?;
+                    res = this.binary_op(mir_op, &res, &op)?;
                 }
                 this.write_immediate(*res, dest)?;
             }
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index cbf02d7..0c6a256 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -1025,7 +1025,7 @@
         bin_op: mir::BinOp,
         left: &ImmTy<'tcx, Provenance>,
         right: &ImmTy<'tcx, Provenance>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx, Provenance>, bool)> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
         ecx.binary_ptr_op(bin_op, left, right)
     }
 
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index d99be39..7a00826 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -14,7 +14,7 @@
         bin_op: mir::BinOp,
         left: &ImmTy<'tcx, Provenance>,
         right: &ImmTy<'tcx, Provenance>,
-    ) -> InterpResult<'tcx, (ImmTy<'tcx, Provenance>, bool)> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
         use rustc_middle::mir::BinOp::*;
 
         let this = self.eval_context_ref();
@@ -45,7 +45,7 @@
                     Ge => left >= right,
                     _ => bug!(),
                 };
-                (ImmTy::from_bool(res, *this.tcx), false)
+                ImmTy::from_bool(res, *this.tcx)
             }
 
             // Some more operations are possible with atomics.
@@ -60,16 +60,14 @@
                     right.to_scalar().to_target_usize(this)?,
                     this.machine.layouts.usize,
                 );
-                let (result, overflowing) = this.overflowing_binary_op(bin_op, &left, &right)?;
+                let result = this.binary_op(bin_op, &left, &right)?;
                 // Construct a new pointer with the provenance of `ptr` (the LHS).
                 let result_ptr = Pointer::new(
                     ptr.provenance,
                     Size::from_bytes(result.to_scalar().to_target_usize(this)?),
                 );
-                (
-                    ImmTy::from_scalar(Scalar::from_maybe_pointer(result_ptr, this), left.layout),
-                    overflowing,
-                )
+
+                ImmTy::from_scalar(Scalar::from_maybe_pointer(result_ptr, this), left.layout)
             }
 
             _ => span_bug!(this.cur_span(), "Invalid operator on pointers: {:?}", bin_op),
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index 48b7222..74c2b0c 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -50,13 +50,16 @@
                 let a = this.read_immediate(a)?;
                 let b = this.read_immediate(b)?;
 
-                let (sum, overflow1) = this.overflowing_binary_op(mir::BinOp::Add, &a, &b)?;
-                let (sum, overflow2) = this.overflowing_binary_op(
-                    mir::BinOp::Add,
-                    &sum,
-                    &ImmTy::from_uint(c_in, a.layout),
-                )?;
-                let c_out = overflow1 | overflow2;
+                let (sum, overflow1) =
+                    this.binary_op(mir::BinOp::AddWithOverflow, &a, &b)?.to_pair(this);
+                let (sum, overflow2) = this
+                    .binary_op(
+                        mir::BinOp::AddWithOverflow,
+                        &sum,
+                        &ImmTy::from_uint(c_in, a.layout),
+                    )?
+                    .to_pair(this);
+                let c_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
 
                 this.write_scalar(Scalar::from_u8(c_out.into()), &this.project_field(dest, 0)?)?;
                 this.write_immediate(*sum, &this.project_field(dest, 1)?)?;
@@ -76,13 +79,11 @@
                 let a = this.read_immediate(a)?;
                 let b = this.read_immediate(b)?;
 
-                let (sub, overflow1) = this.overflowing_binary_op(mir::BinOp::Sub, &a, &b)?;
-                let (sub, overflow2) = this.overflowing_binary_op(
-                    mir::BinOp::Sub,
-                    &sub,
-                    &ImmTy::from_uint(b_in, a.layout),
-                )?;
-                let b_out = overflow1 | overflow2;
+                let (sub, overflow1) = this.binary_op(mir::BinOp::SubWithOverflow, &a, &b)?.to_pair(this);
+                let (sub, overflow2) = this
+                    .binary_op(mir::BinOp::SubWithOverflow, &sub, &ImmTy::from_uint(b_in, a.layout))?
+                    .to_pair(this);
+                let b_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
 
                 this.write_scalar(Scalar::from_u8(b_out.into()), &this.project_field(dest, 0)?)?;
                 this.write_immediate(*sub, &this.project_field(dest, 1)?)?;
@@ -245,7 +246,7 @@
 ) -> InterpResult<'tcx, Scalar<Provenance>> {
     match which {
         FloatBinOp::Arith(which) => {
-            let res = this.wrapping_binary_op(which, left, right)?;
+            let res = this.binary_op(which, left, right)?;
             Ok(res.to_scalar())
         }
         FloatBinOp::Cmp { gt, lt, eq, unord } => {
@@ -744,12 +745,9 @@
         let op = this.read_immediate(&this.project_index(&op, i)?)?;
         let dest = this.project_index(&dest, i)?;
 
-        let lt_zero = this.wrapping_binary_op(mir::BinOp::Lt, &op, &zero)?;
-        let res = if lt_zero.to_scalar().to_bool()? {
-            this.wrapping_unary_op(mir::UnOp::Neg, &op)?
-        } else {
-            op
-        };
+        let lt_zero = this.binary_op(mir::BinOp::Lt, &op, &zero)?;
+        let res =
+            if lt_zero.to_scalar().to_bool()? { this.unary_op(mir::UnOp::Neg, &op)? } else { op };
 
         this.write_immediate(*res, &dest)?;
     }
@@ -832,7 +830,7 @@
             let res = if saturating {
                 Immediate::from(this.saturating_arith(which, &lhs, &rhs)?)
             } else {
-                *this.wrapping_binary_op(which, &lhs, &rhs)?
+                *this.binary_op(which, &lhs, &rhs)?
             };
 
             this.write_immediate(res, &this.project_index(&dest, j)?)?;
@@ -884,8 +882,8 @@
                 let left = this.read_immediate(&this.project_index(&left, j)?)?;
                 let right = this.read_immediate(&this.project_index(&right, j)?)?;
 
-                let mul = this.wrapping_binary_op(mir::BinOp::Mul, &left, &right)?;
-                sum = this.wrapping_binary_op(mir::BinOp::Add, &sum, &mul)?;
+                let mul = this.binary_op(mir::BinOp::Mul, &left, &right)?;
+                sum = this.binary_op(mir::BinOp::Add, &sum, &mul)?;
             }
         }
 
@@ -1276,11 +1274,8 @@
         let left = this.read_immediate(&this.project_index(&left, i)?)?;
         let right = this.read_scalar(&this.project_index(&right, i)?)?.to_int(dest.layout.size)?;
 
-        let res = this.wrapping_binary_op(
-            mir::BinOp::Mul,
-            &left,
-            &ImmTy::from_int(right.signum(), dest.layout),
-        )?;
+        let res =
+            this.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
 
         this.write_immediate(*res, &dest)?;
     }
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs
deleted file mode 100644
index a1fefe0..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Make sure we find these even with many checks disabled.
-//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
-
-fn main() {
-    let p = {
-        let b = Box::new(42);
-        &*b as *const i32 as *const ()
-    };
-    let _x = unsafe { *p }; //~ ERROR: has been freed
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr
deleted file mode 100644
index 72b9a4a..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr
+++ /dev/null
@@ -1,25 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
-  --> $DIR/dangling_zst_deref.rs:LL:CC
-   |
-LL |     let _x = unsafe { *p };
-   |                       ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/dangling_zst_deref.rs:LL:CC
-   |
-LL |         let b = Box::new(42);
-   |                 ^^^^^^^^^^^^
-help: ALLOC was deallocated here:
-  --> $DIR/dangling_zst_deref.rs:LL:CC
-   |
-LL |     };
-   |     ^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/dangling_zst_deref.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs
deleted file mode 100644
index 73d0b12..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-fn main() {
-    // This pointer *could* be NULL so we cannot load from it, not even at ZST
-    let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const ();
-    let _x: () = unsafe { *ptr }; //~ ERROR: out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr
deleted file mode 100644
index 13c53e2..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-  --> $DIR/maybe_null_pointer_deref_zst.rs:LL:CC
-   |
-LL |     let _x: () = unsafe { *ptr };
-   |                           ^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/maybe_null_pointer_deref_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs
deleted file mode 100644
index 5537207..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-fn main() {
-    // This pointer *could* be NULL so we cannot load from it, not even at ZST.
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-    let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0];
-    unsafe { *ptr = zst_val }; //~ ERROR: out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr
deleted file mode 100644
index e4e23e8..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-  --> $DIR/maybe_null_pointer_write_zst.rs:LL:CC
-   |
-LL |     unsafe { *ptr = zst_val };
-   |              ^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/maybe_null_pointer_write_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs
deleted file mode 100644
index f8af43f..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[allow(deref_nullptr)]
-fn main() {
-    let x: () = unsafe { *std::ptr::null() }; //~ ERROR: memory access failed: null pointer is a dangling pointer
-    panic!("this should never print: {:?}", x);
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr
deleted file mode 100644
index 1a8794f..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/null_pointer_deref_zst.rs:LL:CC
-   |
-LL |     let x: () = unsafe { *std::ptr::null() };
-   |                          ^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/null_pointer_deref_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs
deleted file mode 100644
index edd6c8f..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-#[allow(deref_nullptr)]
-fn main() {
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-    unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
-    //~^ERROR: memory access failed: null pointer is a dangling pointer
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr
deleted file mode 100644
index 1d4704e..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/null_pointer_write_zst.rs:LL:CC
-   |
-LL |     unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/null_pointer_write_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr b/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr
deleted file mode 100644
index 699dda5..0000000
--- a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: unsupported operation: miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that
-  --> $DIR/intrinsic_fallback_checks_ub.rs:LL:CC
-   |
-LL |     ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/intrinsic_fallback_checks_ub.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs
similarity index 75%
rename from src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs
rename to src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs
index 93c9d3d..888c548 100644
--- a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs
+++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs
@@ -10,5 +10,5 @@
 
 fn main() {
     ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null());
-    //~^ ERROR: can only use intrinsic fallback bodies that check UB.
+    //~^ ERROR: can only use intrinsic fallback bodies that exactly reflect the specification
 }
diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr
new file mode 100644
index 0000000..db3941a
--- /dev/null
+++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr
@@ -0,0 +1,14 @@
+error: unsupported operation: Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that
+  --> $DIR/intrinsic_fallback_is_spec.rs:LL:CC
+   |
+LL |     ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that
+   |
+   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/intrinsic_fallback_is_spec.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.rs b/src/tools/miri/tests/fail/intrinsics/copy_null.rs
deleted file mode 100644
index 237e517..0000000
--- a/src/tools/miri/tests/fail/intrinsics/copy_null.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#![feature(intrinsics)]
-
-// Directly call intrinsic to avoid debug assertions in libstd
-extern "rust-intrinsic" {
-    fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
-}
-
-fn main() {
-    let mut data = [0u16; 4];
-    let ptr = &mut data[0] as *mut u16;
-    // Even copying 0 elements from NULL should error.
-    unsafe {
-        copy_nonoverlapping(std::ptr::null(), ptr, 0); //~ ERROR: memory access failed: null pointer is a dangling pointer
-    }
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr b/src/tools/miri/tests/fail/intrinsics/copy_null.stderr
deleted file mode 100644
index d73c034..0000000
--- a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/copy_null.rs:LL:CC
-   |
-LL |         copy_nonoverlapping(std::ptr::null(), ptr, 0);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/copy_null.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs
deleted file mode 100644
index e2329c1..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//@compile-flags: -Zmiri-permissive-provenance
-
-#[rustfmt::skip] // fails with "left behind trailing whitespace"
-fn main() {
-    let x = 0 as *mut i32;
-    let _x = x.wrapping_offset(8); // ok, this has no inbounds tag
-    let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds
-    //~^ERROR: null pointer is a dangling pointer
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr
deleted file mode 100644
index a8984c7..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/ptr_offset_0_plus_0.rs:LL:CC
-   |
-LL |     let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds
-   |                       ^^^^^^^^^^^ out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/ptr_offset_0_plus_0.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs
deleted file mode 100644
index 0e5acf0..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-fn main() {
-    let start_ptr = &4 as *const _ as *const u8;
-    let length = 10;
-    let end_ptr = start_ptr.wrapping_add(length);
-    // Even if the offset is 0, a dangling OOB pointer is not allowed.
-    unsafe { end_ptr.offset_from(end_ptr) }; //~ERROR: pointer at offset 10 is out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr
deleted file mode 100644
index 32a4461..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds
-  --> $DIR/ptr_offset_from_oob.rs:LL:CC
-   |
-LL |     unsafe { end_ptr.offset_from(end_ptr) };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/ptr_offset_from_oob.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs
deleted file mode 100644
index 575e288..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-#[rustfmt::skip] // fails with "left behind trailing whitespace"
-fn main() {
-    let x = Box::into_raw(Box::new(0u32));
-    let x = x.wrapping_offset(8); // ok, this has no inbounds tag
-    let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to
-    //~^ERROR: pointer at offset 32 is out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr
deleted file mode 100644
index 304d362..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds
-  --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
-   |
-LL |     let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to
-   |                       ^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
-   |
-LL |     let x = Box::into_raw(Box::new(0u32));
-   |                           ^^^^^^^^^^^^^^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs
index 8a49c84..12aa7c10 100644
--- a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs
+++ b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs
@@ -10,6 +10,6 @@
     unsafe {
         let x = i32x2(1, 1);
         let y = i32x2(100, 0);
-        simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in SIMD lane 0
+        simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in lane 0
     }
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr
index 3a4ec00..475067d 100644
--- a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflowing shift by 100 in `simd_shl` in SIMD lane 0
+error: Undefined Behavior: overflowing shift by 100 in `simd_shl` in lane 0
   --> $DIR/simd-shl-too-far.rs:LL:CC
    |
 LL |         simd_shl(x, y);
-   |         ^^^^^^^^^^^^^^ overflowing shift by 100 in `simd_shl` in SIMD lane 0
+   |         ^^^^^^^^^^^^^^ overflowing shift by 100 in `simd_shl` in lane 0
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs
index 433998c..ada7cf4 100644
--- a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs
+++ b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs
@@ -10,6 +10,6 @@
     unsafe {
         let x = i32x2(1, 1);
         let y = i32x2(20, 40);
-        simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in SIMD lane 1
+        simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in lane 1
     }
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr
index 07636b8..0d63078 100644
--- a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflowing shift by 40 in `simd_shr` in SIMD lane 1
+error: Undefined Behavior: overflowing shift by 40 in `simd_shr` in lane 1
   --> $DIR/simd-shr-too-far.rs:LL:CC
    |
 LL |         simd_shr(x, y);
-   |         ^^^^^^^^^^^^^^ overflowing shift by 40 in `simd_shr` in SIMD lane 1
+   |         ^^^^^^^^^^^^^^ overflowing shift by 40 in `simd_shr` in lane 1
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs
index 3f8b4e5..0ed758d 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs
@@ -1,4 +1,4 @@
 fn main() {
     // MAX overflow
-    let _val = unsafe { 40000u16.unchecked_add(30000) }; //~ ERROR: overflow executing `unchecked_add`
+    let _val = unsafe { 40000u16.unchecked_add(30000) }; //~ ERROR: arithmetic overflow in `unchecked_add`
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr
index 922de42..eae9ec7 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflow executing `unchecked_add`
+error: Undefined Behavior: arithmetic overflow in `unchecked_add`
   --> $DIR/unchecked_add1.rs:LL:CC
    |
 LL |     let _val = unsafe { 40000u16.unchecked_add(30000) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_add`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_add`
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs
index 3283dbf..3b49520 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs
@@ -1,4 +1,4 @@
 fn main() {
     // MIN overflow
-    let _val = unsafe { (-30000i16).unchecked_add(-8000) }; //~ ERROR: overflow executing `unchecked_add`
+    let _val = unsafe { (-30000i16).unchecked_add(-8000) }; //~ ERROR: arithmetic overflow in `unchecked_add`
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr
index 05456ea..6a0dcfc 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflow executing `unchecked_add`
+error: Undefined Behavior: arithmetic overflow in `unchecked_add`
   --> $DIR/unchecked_add2.rs:LL:CC
    |
 LL |     let _val = unsafe { (-30000i16).unchecked_add(-8000) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_add`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_add`
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs
index 2feed77..3dc83a2 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs
@@ -1,4 +1,4 @@
 fn main() {
     // MAX overflow
-    let _val = unsafe { 300u16.unchecked_mul(250u16) }; //~ ERROR: overflow executing `unchecked_mul`
+    let _val = unsafe { 300u16.unchecked_mul(250u16) }; //~ ERROR: arithmetic overflow in `unchecked_mul`
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr
index 533beaa..e37d982 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflow executing `unchecked_mul`
+error: Undefined Behavior: arithmetic overflow in `unchecked_mul`
   --> $DIR/unchecked_mul1.rs:LL:CC
    |
 LL |     let _val = unsafe { 300u16.unchecked_mul(250u16) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_mul`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_mul`
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs
index 42cd509..49cd293 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs
@@ -1,4 +1,4 @@
 fn main() {
     // MIN overflow
-    let _val = unsafe { 1_000_000_000i32.unchecked_mul(-4) }; //~ ERROR: overflow executing `unchecked_mul`
+    let _val = unsafe { 1_000_000_000i32.unchecked_mul(-4) }; //~ ERROR: arithmetic overflow in `unchecked_mul`
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr
index 4c6bae6..949077c 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflow executing `unchecked_mul`
+error: Undefined Behavior: arithmetic overflow in `unchecked_mul`
   --> $DIR/unchecked_mul2.rs:LL:CC
    |
 LL |     let _val = unsafe { 1_000_000_000i32.unchecked_mul(-4) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_mul`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_mul`
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs
index e5178bf..f24c974 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs
@@ -1,4 +1,4 @@
 fn main() {
     // MIN overflow
-    let _val = unsafe { 14u32.unchecked_sub(22) }; //~ ERROR: overflow executing `unchecked_sub`
+    let _val = unsafe { 14u32.unchecked_sub(22) }; //~ ERROR: arithmetic overflow in `unchecked_sub`
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr
index 72187e2..39bfd8a 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflow executing `unchecked_sub`
+error: Undefined Behavior: arithmetic overflow in `unchecked_sub`
   --> $DIR/unchecked_sub1.rs:LL:CC
    |
 LL |     let _val = unsafe { 14u32.unchecked_sub(22) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_sub`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_sub`
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs
index ac9fd1e..74b08b1 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs
@@ -1,4 +1,4 @@
 fn main() {
     // MAX overflow
-    let _val = unsafe { 30000i16.unchecked_sub(-7000) }; //~ ERROR: overflow executing `unchecked_sub`
+    let _val = unsafe { 30000i16.unchecked_sub(-7000) }; //~ ERROR: arithmetic overflow in `unchecked_sub`
 }
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr
index ec28384..604eba9 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: overflow executing `unchecked_sub`
+error: Undefined Behavior: arithmetic overflow in `unchecked_sub`
   --> $DIR/unchecked_sub2.rs:LL:CC
    |
 LL |     let _val = unsafe { 30000i16.unchecked_sub(-7000) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_sub`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_sub`
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs
deleted file mode 100644
index 2f46c82..0000000
--- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-#![feature(intrinsics)]
-
-// Directly call intrinsic to avoid debug assertions in libstd
-extern "rust-intrinsic" {
-    fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
-}
-
-fn main() {
-    unsafe { write_bytes::<u8>(std::ptr::null_mut(), 0, 0) }; //~ ERROR: memory access failed: null pointer is a dangling pointer
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr
deleted file mode 100644
index def1809..0000000
--- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/write_bytes_null.rs:LL:CC
-   |
-LL |     unsafe { write_bytes::<u8>(std::ptr::null_mut(), 0, 0) };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/write_bytes_null.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/zst2.rs b/src/tools/miri/tests/fail/zst2.rs
deleted file mode 100644
index 04218c2..0000000
--- a/src/tools/miri/tests/fail/zst2.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-fn main() {
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-
-    // make sure ZST accesses are checked against being "truly" dangling pointers
-    // (into deallocated allocations).
-    let mut x_box = Box::new(1u8);
-    let x = &mut *x_box as *mut _ as *mut [u8; 0];
-    drop(x_box);
-    unsafe { *x = zst_val }; //~ ERROR: has been freed
-}
diff --git a/src/tools/miri/tests/fail/zst2.stderr b/src/tools/miri/tests/fail/zst2.stderr
deleted file mode 100644
index b3f65e7..0000000
--- a/src/tools/miri/tests/fail/zst2.stderr
+++ /dev/null
@@ -1,25 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
-  --> $DIR/zst2.rs:LL:CC
-   |
-LL |     unsafe { *x = zst_val };
-   |              ^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/zst2.rs:LL:CC
-   |
-LL |     let mut x_box = Box::new(1u8);
-   |                     ^^^^^^^^^^^^^
-help: ALLOC was deallocated here:
-  --> $DIR/zst2.rs:LL:CC
-   |
-LL |     drop(x_box);
-   |     ^^^^^^^^^^^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/zst2.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/zst3.rs b/src/tools/miri/tests/fail/zst3.rs
deleted file mode 100644
index 454bef2..0000000
--- a/src/tools/miri/tests/fail/zst3.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-fn main() {
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-
-    // make sure ZST accesses are checked against being "truly" dangling pointers
-    // (that are out-of-bounds).
-    let mut x_box = Box::new(1u8);
-    let x = (&mut *x_box as *mut u8).wrapping_offset(1);
-    // This one is just "at the edge", but still okay
-    unsafe { *(x as *mut [u8; 0]) = zst_val };
-    // One byte further is OOB.
-    let x = x.wrapping_offset(1);
-    unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR: out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/zst3.stderr b/src/tools/miri/tests/fail/zst3.stderr
deleted file mode 100644
index b9495fb..0000000
--- a/src/tools/miri/tests/fail/zst3.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds
-  --> $DIR/zst3.rs:LL:CC
-   |
-LL |     unsafe { *(x as *mut [u8; 0]) = zst_val };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/zst3.rs:LL:CC
-   |
-LL |     let mut x_box = Box::new(1u8);
-   |                     ^^^^^^^^^^^^^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/zst3.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/zst1.rs b/src/tools/miri/tests/fail/zst_local_oob.rs
similarity index 100%
rename from src/tools/miri/tests/fail/zst1.rs
rename to src/tools/miri/tests/fail/zst_local_oob.rs
diff --git a/src/tools/miri/tests/fail/zst1.stderr b/src/tools/miri/tests/fail/zst_local_oob.stderr
similarity index 88%
rename from src/tools/miri/tests/fail/zst1.stderr
rename to src/tools/miri/tests/fail/zst_local_oob.stderr
index cda837d..ba1ccaa 100644
--- a/src/tools/miri/tests/fail/zst1.stderr
+++ b/src/tools/miri/tests/fail/zst_local_oob.stderr
@@ -1,5 +1,5 @@
 error: Undefined Behavior: memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
-  --> $DIR/zst1.rs:LL:CC
+  --> $DIR/zst_local_oob.rs:LL:CC
    |
 LL |     let _val = unsafe { *x };
    |                         ^^ memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
@@ -7,7 +7,7 @@
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
    = note: BACKTRACE:
-   = note: inside `main` at $DIR/zst1.rs:LL:CC
+   = note: inside `main` at $DIR/zst_local_oob.rs:LL:CC
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs
index dec3d77..9647277 100644
--- a/src/tools/miri/tests/pass/align_offset_symbolic.rs
+++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs
@@ -118,10 +118,9 @@
     let parts: (*const (), *const u8) = unsafe { mem::transmute(ptr) };
     let vtable = parts.1;
     let offset = vtable.align_offset(mem::align_of::<TWOPTR>());
-    let _vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0];
-    // FIXME: we can't actually do the access since vtable pointers act like zero-sized allocations.
-    // Enable the next line once https://github.com/rust-lang/rust/issues/117945 is implemented.
-    //let _place = unsafe { &*vtable_aligned };
+    let vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0];
+    // Zero-sized deref, so no in-bounds requirement.
+    let _place = unsafe { &*vtable_aligned };
 }
 
 fn main() {
diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
new file mode 100644
index 0000000..2d142be
--- /dev/null
+++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
@@ -0,0 +1,59 @@
+//! Tests specific for <https://github.com/rust-lang/rust/issues/117945>: zero-sized operations.
+#![feature(strict_provenance)]
+
+use std::ptr;
+
+fn main() {
+    // Null.
+    test_ptr(ptr::null_mut::<()>());
+    // No provenance.
+    test_ptr(ptr::without_provenance_mut::<()>(1));
+    // Out-of-bounds.
+    let mut b = Box::new(0i32);
+    let ptr = ptr::addr_of_mut!(*b) as *mut ();
+    test_ptr(ptr.wrapping_byte_add(2));
+    // Dangling (use-after-free).
+    drop(b);
+    test_ptr(ptr);
+}
+
+fn test_ptr(ptr: *mut ()) {
+    unsafe {
+        // Reads and writes.
+        let mut val = *ptr;
+        *ptr = val;
+        ptr.read();
+        ptr.write(());
+        // Memory access intrinsics.
+        // - memcpy (1st and 2nd argument)
+        ptr.copy_from_nonoverlapping(&(), 1);
+        ptr.copy_to_nonoverlapping(&mut val, 1);
+        // - memmove (1st and 2nd argument)
+        ptr.copy_from(&(), 1);
+        ptr.copy_to(&mut val, 1);
+        // - memset
+        ptr.write_bytes(0u8, 1);
+        // Offset.
+        let _ = ptr.offset(0);
+        let _ = ptr.offset(1); // this is still 0 bytes
+        // Distance.
+        let ptr = ptr.cast::<i32>();
+        ptr.offset_from(ptr);
+        /*
+        FIXME: this is disabled for now as these cases are not yet allowed.
+        // Distance from other "bad" pointers that have the same address, but different provenance. Some
+        // of this is library UB, but we don't want it to be language UB since that would violate
+        // provenance monotonicity: if we allow computing the distance between two ptrs with no
+        // provenance, we have to allow computing it between two ptrs with arbitrary provenance.
+        // - Distance from "no provenance"
+        ptr.offset_from(ptr::without_provenance_mut(ptr.addr()));
+        // - Distance from out-of-bounds pointer
+        let mut b = Box::new(0i32);
+        let other_ptr = ptr::addr_of_mut!(*b);
+        ptr.offset_from(other_ptr.with_addr(ptr.addr()));
+        // - Distance from use-after-free pointer
+        drop(b);
+        ptr.offset_from(other_ptr.with_addr(ptr.addr()));
+        */
+    }
+}
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 96fb8e2..72192f8 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -111,7 +111,6 @@
 run-make/issue-37893/Makefile
 run-make/issue-38237/Makefile
 run-make/issue-40535/Makefile
-run-make/issue-46239/Makefile
 run-make/issue-47384/Makefile
 run-make/issue-47551/Makefile
 run-make/issue-51671/Makefile
diff --git a/tests/codegen/noalias-freeze.rs b/tests/codegen/noalias-freeze.rs
new file mode 100644
index 0000000..8086f3a
--- /dev/null
+++ b/tests/codegen/noalias-freeze.rs
@@ -0,0 +1,21 @@
+//@ compile-flags: -Copt-level=1
+
+// References returned by a Frozen pointer type
+// could be marked as "noalias", which caused miscompilation errors.
+// This test runs the most minimal possible code that can reproduce this bug,
+// and checks that noalias does not appear.
+// See https://github.com/rust-lang/rust/issues/46239
+
+#![crate_type = "lib"]
+
+fn project<T>(x: &(T,)) -> &T { &x.0 }
+
+fn dummy() {}
+
+// CHECK-LABEL: @foo(
+// CHECK-NOT: noalias
+#[no_mangle]
+pub fn foo() {
+    let f = (dummy as fn(),);
+    (*project(&f))();
+}
diff --git a/tests/crashes/124348.rs b/tests/crashes/124348.rs
deleted file mode 100644
index 554f383..0000000
--- a/tests/crashes/124348.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@ known-bug: #124348
-enum Eek {
-    TheConst,
-    UnusedByTheConst(Sum),
-}
-
-const EEK_ZERO: &[Eek] = &[];
diff --git a/tests/run-make/issue-46239/Makefile b/tests/run-make/issue-46239/Makefile
deleted file mode 100644
index 0006ced..0000000
--- a/tests/run-make/issue-46239/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-all:
-	$(RUSTC) main.rs -C opt-level=1
-	$(call RUN,main)
diff --git a/tests/run-make/issue-46239/main.rs b/tests/run-make/issue-46239/main.rs
deleted file mode 100644
index b7df5cf..0000000
--- a/tests/run-make/issue-46239/main.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-fn project<T>(x: &(T,)) -> &T { &x.0 }
-
-fn dummy() {}
-
-fn main() {
-    let f = (dummy as fn(),);
-    (*project(&f))();
-}
diff --git a/tests/rustdoc/issue-95633.rs b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs
similarity index 71%
rename from tests/rustdoc/issue-95633.rs
rename to tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs
index 5695ef5..d53a67c 100644
--- a/tests/rustdoc/issue-95633.rs
+++ b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs
@@ -1,6 +1,8 @@
+//@ check-pass
 //@ compile-flags: --document-private-items
 
 // This ensures that no ICE is triggered when rustdoc is run on this code.
+// https://github.com/rust-lang/rust/issues/95633
 
 mod stdlib {
     pub (crate) use std::i8;
diff --git a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs b/tests/rustdoc/asref-for-and-of-local-82465.rs
similarity index 63%
rename from tests/rustdoc/issue-82465-asref-for-and-of-local.rs
rename to tests/rustdoc/asref-for-and-of-local-82465.rs
index adf4d11..e620468 100644
--- a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs
+++ b/tests/rustdoc/asref-for-and-of-local-82465.rs
@@ -1,7 +1,10 @@
+// https://github.com/rust-lang/rust/issues/82465
+#![crate_name = "foo"]
+
 use std::convert::AsRef;
 pub struct Local;
 
-// @has issue_82465_asref_for_and_of_local/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef<str> for Local'
+// @has foo/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef<str> for Local'
 impl AsRef<str> for Local {
     fn as_ref(&self) -> &str {
         todo!()
diff --git a/tests/rustdoc/issue-88600.rs b/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs
similarity index 93%
rename from tests/rustdoc/issue-88600.rs
rename to tests/rustdoc/enum-variant-doc-hidden-field-88600.rs
index f89af47..31d96e9 100644
--- a/tests/rustdoc/issue-88600.rs
+++ b/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs
@@ -1,4 +1,6 @@
 // This test ensure that #[doc(hidden)] is applied correctly in enum variant fields.
+// https://github.com/rust-lang/rust/issues/88600
+#![crate_name = "foo"]
 
 // Denotes a field which should be hidden.
 pub struct H;
@@ -6,7 +8,7 @@
 // Denotes a field which should not be hidden (shown).
 pub struct S;
 
-// @has issue_88600/enum.FooEnum.html
+// @has foo/enum.FooEnum.html
 pub enum FooEnum {
     // @has - '//*[@id="variant.HiddenTupleItem"]//h3' 'HiddenTupleItem(/* private fields */)'
     // @count - '//*[@id="variant.HiddenTupleItem.field.0"]' 0
diff --git a/tests/rustdoc/issue-89309-heading-levels.rs b/tests/rustdoc/heading-levels-89309.rs
similarity index 92%
rename from tests/rustdoc/issue-89309-heading-levels.rs
rename to tests/rustdoc/heading-levels-89309.rs
index bb706c2..caa9942 100644
--- a/tests/rustdoc/issue-89309-heading-levels.rs
+++ b/tests/rustdoc/heading-levels-89309.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/89309
 #![crate_name = "foo"]
 
 // @has foo/trait.Read.html
diff --git a/tests/rustdoc/auxiliary/issue-85454.rs b/tests/rustdoc/inline_cross/auxiliary/issue-85454.rs
similarity index 100%
rename from tests/rustdoc/auxiliary/issue-85454.rs
rename to tests/rustdoc/inline_cross/auxiliary/issue-85454.rs
diff --git a/tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs b/tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs
similarity index 100%
rename from tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs
rename to tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs
diff --git a/tests/rustdoc/issue-85454.rs b/tests/rustdoc/inline_cross/qpath-self-85454.rs
similarity index 94%
rename from tests/rustdoc/issue-85454.rs
rename to tests/rustdoc/inline_cross/qpath-self-85454.rs
index 790db0c..de806db 100644
--- a/tests/rustdoc/issue-85454.rs
+++ b/tests/rustdoc/inline_cross/qpath-self-85454.rs
@@ -1,6 +1,7 @@
 //@ aux-build:issue-85454.rs
 //@ build-aux-docs
 #![crate_name = "foo"]
+// https://github.com/rust-lang/rust/issues/85454
 
 extern crate issue_85454;
 
diff --git a/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs
new file mode 100644
index 0000000..fe6e5a3
--- /dev/null
+++ b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs
@@ -0,0 +1,18 @@
+//@ aux-build:reexport-with-anonymous-lifetime-98697.rs
+//@ ignore-cross-compile
+#![crate_name = "foo"]
+
+// When reexporting a function with a HRTB with anonymous lifetimes,
+// make sure the anonymous lifetimes are not rendered.
+//
+// https://github.com/rust-lang/rust/issues/98697
+
+extern crate reexport_with_anonymous_lifetime_98697;
+
+// @has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro<F>()where F: Fn(&str)'
+// @!has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<'
+pub use reexport_with_anonymous_lifetime_98697::repro;
+
+// @has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra'
+// @!has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<'
+pub use reexport_with_anonymous_lifetime_98697::Extra;
diff --git a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs b/tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs
similarity index 94%
rename from tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs
rename to tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs
index 95ddd4c..343e030 100644
--- a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs
+++ b/tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs
@@ -2,6 +2,7 @@
 // This test ensures that a publicly re-exported private trait will
 // appear in the blanket impl list.
 
+// https://github.com/rust-lang/rust/issues/94183
 #![crate_name = "foo"]
 
 // @has 'foo/struct.S.html'
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs
similarity index 84%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs
index fba310c..c066f54 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs
@@ -1,5 +1,6 @@
 //@ edition:2015
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use external::Public as Private;
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs
similarity index 97%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs
index bd54d02..d695ed7 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs
@@ -1,6 +1,7 @@
 // This test ensures that if a private re-export is present in a public API, it'll be
 // replaced by the first public item in the re-export chain or by the private item.
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use crate::bar::Bar as Alias;
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs
similarity index 85%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs
index 7e28950..1c86c76 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use crate::bar::Foo as Alias;
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs
similarity index 85%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs
index 388f69b..7d6fadf 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs
@@ -1,5 +1,6 @@
 //@ compile-flags: -Z unstable-options --document-hidden-items
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 #[doc(hidden)]
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs
similarity index 93%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs
index 2633f98..6bf5078 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs
@@ -1,5 +1,6 @@
 //@ compile-flags: --document-private-items
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use crate::bar::Bar as Alias;
diff --git a/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs b/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs
new file mode 100644
index 0000000..cffe128
--- /dev/null
+++ b/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs
@@ -0,0 +1,16 @@
+//@ edition:2018
+
+// https://github.com/rust-lang/rust/issues/89852
+#![crate_name = "foo"]
+#![no_core]
+#![feature(no_core)]
+
+// @matchesraw 'foo/sidebar-items.js' '"repro"'
+// @!matchesraw 'foo/sidebar-items.js' '"repro".*"repro"'
+
+#[macro_export]
+macro_rules! repro {
+    () => {};
+}
+
+pub use crate::repro as repro2;
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs
deleted file mode 100644
index d0960df..0000000
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![crate_name = "foo"]
-
-pub mod sub {
-    pub struct Item;
-
-    pub mod prelude {
-        pub use super::Item;
-    }
-}
-
-// @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
-// @count foo/prelude/index.html '//div[@class="item-row"]' 0
-pub mod prelude {}
-
-#[doc(inline)]
-pub use sub::*;
diff --git a/tests/rustdoc/issue-86620.rs b/tests/rustdoc/issue-86620.rs
deleted file mode 100644
index a7ac0f1..0000000
--- a/tests/rustdoc/issue-86620.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//@ aux-build:issue-86620-1.rs
-
-extern crate issue_86620_1;
-
-use issue_86620_1::*;
-
-// @!has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
-// @has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
-pub struct S;
diff --git a/tests/rustdoc/issue-89852.rs b/tests/rustdoc/issue-89852.rs
deleted file mode 100644
index e9b3d80..0000000
--- a/tests/rustdoc/issue-89852.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//@ edition:2018
-
-#![no_core]
-#![feature(no_core)]
-
-// @matchesraw 'issue_89852/sidebar-items.js' '"repro"'
-// @!matchesraw 'issue_89852/sidebar-items.js' '"repro".*"repro"'
-
-#[macro_export]
-macro_rules! repro {
-    () => {};
-}
-
-pub use crate::repro as repro2;
diff --git a/tests/rustdoc/issue-95873.rs b/tests/rustdoc/issue-95873.rs
deleted file mode 100644
index 83f1f2f..0000000
--- a/tests/rustdoc/issue-95873.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-// @has issue_95873/index.html "//*[@class='item-name']" "pub use ::std as x;"
-pub use ::std as x;
diff --git a/tests/rustdoc/issue-98697.rs b/tests/rustdoc/issue-98697.rs
deleted file mode 100644
index df9f291..0000000
--- a/tests/rustdoc/issue-98697.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-//@ aux-build:issue-98697-reexport-with-anonymous-lifetime.rs
-//@ ignore-cross-compile
-
-// When reexporting a function with a HRTB with anonymous lifetimes,
-// make sure the anonymous lifetimes are not rendered.
-//
-// https://github.com/rust-lang/rust/issues/98697
-
-extern crate issue_98697_reexport_with_anonymous_lifetime;
-
-// @has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro<F>()where F: Fn(&str)'
-// @!has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<'
-pub use issue_98697_reexport_with_anonymous_lifetime::repro;
-
-// @has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra'
-// @!has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<'
-pub use issue_98697_reexport_with_anonymous_lifetime::Extra;
diff --git a/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs b/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs
new file mode 100644
index 0000000..537dadd
--- /dev/null
+++ b/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs
@@ -0,0 +1,11 @@
+//@ aux-build:issue-86620-1.rs
+#![crate_name = "foo"]
+// https://github.com/rust-lang/rust/issues/86620
+
+extern crate issue_86620_1;
+
+use issue_86620_1::*;
+
+// @!has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
+// @has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
+pub struct S;
diff --git a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs b/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs
similarity index 85%
rename from tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs
rename to tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs
index d3ccd1c..ed1e42c 100644
--- a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs
+++ b/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs
@@ -2,6 +2,7 @@
 //@ build-aux-docs
 //@ ignore-cross-compile
 
+// https://github.com/rust-lang/rust/issues/99221
 #![crate_name = "foo"]
 
 #[macro_use]
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs
similarity index 85%
copy from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
copy to tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs
index 9bce258..9b3dfd4 100644
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
+++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/83375
 #![crate_name = "foo"]
 
 pub mod sub {
@@ -8,9 +9,9 @@
     }
 }
 
-#[doc(inline)]
-pub use sub::*;
-
 // @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
 // @count foo/prelude/index.html '//div[@class="item-row"]' 0
 pub mod prelude {}
+
+#[doc(inline)]
+pub use sub::*;
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs
similarity index 85%
rename from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
rename to tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs
index 9bce258..7bad825 100644
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
+++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/83375
 #![crate_name = "foo"]
 
 pub mod sub {
diff --git a/tests/rustdoc/pub-use-root-path-95873.rs b/tests/rustdoc/pub-use-root-path-95873.rs
new file mode 100644
index 0000000..5a817fb
--- /dev/null
+++ b/tests/rustdoc/pub-use-root-path-95873.rs
@@ -0,0 +1,5 @@
+// https://github.com/rust-lang/rust/issues/95873
+#![crate_name = "foo"]
+
+// @has foo/index.html "//*[@class='item-name']" "pub use ::std as x;"
+pub use ::std as x;
diff --git a/tests/rustdoc/issue-80233-normalize-auto-trait.rs b/tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs
similarity index 85%
rename from tests/rustdoc/issue-80233-normalize-auto-trait.rs
rename to tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs
index 62fbc24..0649801 100644
--- a/tests/rustdoc/issue-80233-normalize-auto-trait.rs
+++ b/tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs
@@ -1,7 +1,9 @@
 // Regression test for issue #80233
 // Tests that we don't ICE when processing auto traits
+// https://github.com/rust-lang/rust/issues/80233
 
 #![crate_type = "lib"]
+#![crate_name = "foo"]
 pub trait Trait1 {}
 
 pub trait Trait2 {
@@ -30,7 +32,7 @@
 
 pub struct Struct1 {}
 
-// @has issue_80233_normalize_auto_trait/struct.Question.html
+// @has foo/struct.Question.html
 // @has - '//h3[@class="code-header"]' 'impl<T> Send for Question<T>'
 pub struct Question<T: Trait1> {
     pub ins: <<Vec<T> as Trait3>::Type3 as Trait2>::Type2,
diff --git a/tests/rustdoc/issue-96381.rs b/tests/rustdoc/underscore-type-in-trait-impl-96381.rs
similarity index 85%
rename from tests/rustdoc/issue-96381.rs
rename to tests/rustdoc/underscore-type-in-trait-impl-96381.rs
index 90875c0..6d6e5e0 100644
--- a/tests/rustdoc/issue-96381.rs
+++ b/tests/rustdoc/underscore-type-in-trait-impl-96381.rs
@@ -1,4 +1,5 @@
 //@ should-fail
+// https://github.com/rust-lang/rust/issues/96381
 
 #![allow(unused)]
 
diff --git a/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs
new file mode 100644
index 0000000..ce49f55
--- /dev/null
+++ b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs
@@ -0,0 +1,27 @@
+//@ aux-build:block-on.rs
+//@ edition:2021
+//@ check-pass
+
+#![feature(async_closure)]
+
+extern crate block_on;
+
+fn consume(_: String) {}
+
+fn main() {
+    block_on::block_on(async {
+        let x = 1i32;
+        let s = String::new();
+        // `consume(s)` pulls the closure's kind down to `FnOnce`,
+        // which means that we don't treat the borrow of `x` as a
+        // self-borrow (with `'env` lifetime). This leads to a lifetime
+        // error which is solved by forcing the inner coroutine to
+        // be `move` as well, so that it moves `x`.
+        let c = async move || {
+            println!("{x}");
+            // This makes the closure FnOnce...
+            consume(s);
+        };
+        c().await;
+    });
+}
diff --git a/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs
new file mode 100644
index 0000000..803c990
--- /dev/null
+++ b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs
@@ -0,0 +1,24 @@
+//@ aux-build:block-on.rs
+//@ edition:2021
+//@ check-pass
+
+#![feature(async_closure)]
+
+extern crate block_on;
+
+fn force_fnonce<T: async FnOnce()>(t: T) -> T { t }
+
+fn main() {
+    block_on::block_on(async {
+        let x = 1i32;
+        // `force_fnonce` pulls the closure's kind down to `FnOnce`,
+        // which means that we don't treat the borrow of `x` as a
+        // self-borrow (with `'env` lifetime). This leads to a lifetime
+        // error which is solved by forcing the inner coroutine to
+        // be `move` as well, so that it moves `x`.
+        let c = force_fnonce(async move || {
+            println!("{x}");
+        });
+        c().await;
+    });
+}
diff --git a/tests/ui/async-await/async-closures/implements-fnmut.rs b/tests/ui/async-await/async-closures/implements-fnmut.rs
new file mode 100644
index 0000000..1ed326c
--- /dev/null
+++ b/tests/ui/async-await/async-closures/implements-fnmut.rs
@@ -0,0 +1,23 @@
+//@ check-pass
+//@ edition: 2021
+
+// Demonstrates that an async closure may implement `FnMut` (not just `async FnMut`!)
+// if it has no self-borrows. In this case, `&Ty` is not borrowed from the closure env,
+// since it's fine to reborrow it with its original lifetime. See the doc comment on
+// `should_reborrow_from_env_of_parent_coroutine_closure` for more detail for when we
+// must borrow from the closure env.
+
+#![feature(async_closure)]
+
+fn main() {}
+
+fn needs_fn_mut<T>(x: impl FnMut() -> T) {}
+
+fn hello(x: &Ty) {
+    needs_fn_mut(async || { x.hello(); });
+}
+
+struct Ty;
+impl Ty {
+    fn hello(&self) {}
+}
diff --git a/tests/ui/async-await/async-is-unwindsafe.rs b/tests/ui/async-await/async-is-unwindsafe.rs
index d0202f7..53009b6 100644
--- a/tests/ui/async-await/async-is-unwindsafe.rs
+++ b/tests/ui/async-await/async-is-unwindsafe.rs
@@ -11,7 +11,6 @@
 
     is_unwindsafe(async {
         //~^ ERROR the type `&mut Context<'_>` may not be safely transferred across an unwind boundary
-        //~| ERROR the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
         use std::ptr::null;
         use std::task::{Context, RawWaker, RawWakerVTable, Waker};
         let waker = unsafe {
diff --git a/tests/ui/async-await/async-is-unwindsafe.stderr b/tests/ui/async-await/async-is-unwindsafe.stderr
index 6bb06df..5d87fc7 100644
--- a/tests/ui/async-await/async-is-unwindsafe.stderr
+++ b/tests/ui/async-await/async-is-unwindsafe.stderr
@@ -6,18 +6,19 @@
    |  |_____|
    | ||
 LL | ||
-LL | ||
 LL | ||         use std::ptr::null;
+LL | ||         use std::task::{Context, RawWaker, RawWakerVTable, Waker};
 ...  ||
 LL | ||         drop(cx_ref);
 LL | ||     });
    | ||_____-^ `&mut Context<'_>` may not be safely transferred across an unwind boundary
    |  |_____|
-   |        within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`
+   |        within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`
    |
-   = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe`
+   = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}: UnwindSafe`
+   = note: `UnwindSafe` is implemented for `&Context<'_>`, but not for `&mut Context<'_>`
 note: future does not implement `UnwindSafe` as this value is used across an await
-  --> $DIR/async-is-unwindsafe.rs:26:18
+  --> $DIR/async-is-unwindsafe.rs:25:18
    |
 LL |         let cx_ref = &mut cx;
    |             ------ has type `&mut Context<'_>` which does not implement `UnwindSafe`
@@ -30,38 +31,6 @@
 LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
    |                          ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe`
 
-error[E0277]: the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
-  --> $DIR/async-is-unwindsafe.rs:12:5
-   |
-LL |        is_unwindsafe(async {
-   |   _____^_____________-
-   |  |_____|
-   | ||
-LL | ||
-LL | ||
-LL | ||         use std::ptr::null;
-...  ||
-LL | ||         drop(cx_ref);
-LL | ||     });
-   | ||_____-^ `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary
-   |  |_____|
-   |        within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`
-   |
-   = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut (dyn Any + 'static)`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe`
-note: future does not implement `UnwindSafe` as this value is used across an await
-  --> $DIR/async-is-unwindsafe.rs:26:18
-   |
-LL |         let mut cx = Context::from_waker(&waker);
-   |             ------ has type `Context<'_>` which does not implement `UnwindSafe`
-...
-LL |         async {}.await; // this needs an inner await point
-   |                  ^^^^^ await occurs here, with `mut cx` maybe used later
-note: required by a bound in `is_unwindsafe`
-  --> $DIR/async-is-unwindsafe.rs:3:26
-   |
-LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
-   |                          ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/async-await/context-is-sorta-unwindsafe.rs b/tests/ui/async-await/context-is-sorta-unwindsafe.rs
new file mode 100644
index 0000000..278476e
--- /dev/null
+++ b/tests/ui/async-await/context-is-sorta-unwindsafe.rs
@@ -0,0 +1,14 @@
+//@ run-pass
+// Tests against a regression surfaced by crater in https://github.com/rust-lang/rust/issues/125193
+// Unwind Safety is not a very coherent concept, but we'd prefer no regressions until we kibosh it
+// and this is an unstable feature anyways sooo...
+
+use std::panic::UnwindSafe;
+use std::task::Context;
+
+fn unwind_safe<T: UnwindSafe>() {}
+
+fn main() {
+    unwind_safe::<Context<'_>>(); // test UnwindSafe
+    unwind_safe::<&Context<'_>>(); // test RefUnwindSafe
+}
diff --git a/tests/ui/const-ptr/forbidden_slices.rs b/tests/ui/const-ptr/forbidden_slices.rs
index 85baeed..2550a3a 100644
--- a/tests/ui/const-ptr/forbidden_slices.rs
+++ b/tests/ui/const-ptr/forbidden_slices.rs
@@ -12,7 +12,7 @@
     slice::{from_ptr_range, from_raw_parts},
 };
 
-// Null is never valid for reads
+// Null is never valid for references
 pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) };
 //~^ ERROR: it is undefined behavior to use this value
 pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) };
@@ -46,10 +46,11 @@
 };
 
 pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
-pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
+//~^ ERROR it is undefined behavior to use this value
+pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore
 pub static R2: &[u32] = unsafe {
     let ptr = &D0 as *const u32;
-    from_ptr_range(ptr..ptr.add(2))
+    from_ptr_range(ptr..ptr.add(2)) // errors inside libcore
 };
 pub static R4: &[u8] = unsafe {
     //~^ ERROR: it is undefined behavior to use this value
diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr
index 250366d..eb41a25 100644
--- a/tests/ui/const-ptr/forbidden_slices.stderr
+++ b/tests/ui/const-ptr/forbidden_slices.stderr
@@ -88,20 +88,16 @@
                HEX_DUMP
            }
 
-error[E0080]: could not evaluate static initializer
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-   |
-   = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
-   |
-note: inside `std::ptr::const_ptr::<impl *const u32>::sub_ptr`
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-note: inside `from_ptr_range::<'_, u32>`
-  --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
-note: inside `R0`
-  --> $DIR/forbidden_slices.rs:48:34
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/forbidden_slices.rs:48:1
    |
 LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
 
 error[E0080]: could not evaluate static initializer
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -113,9 +109,9 @@
 note: inside `from_ptr_range::<'_, ()>`
   --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
 note: inside `R1`
-  --> $DIR/forbidden_slices.rs:49:33
+  --> $DIR/forbidden_slices.rs:50:33
    |
-LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
+LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
@@ -127,13 +123,13 @@
 note: inside `std::ptr::const_ptr::<impl *const u32>::add`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
 note: inside `R2`
-  --> $DIR/forbidden_slices.rs:52:25
+  --> $DIR/forbidden_slices.rs:53:25
    |
-LL |     from_ptr_range(ptr..ptr.add(2))
+LL |     from_ptr_range(ptr..ptr.add(2)) // errors inside libcore
    |                         ^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:54:1
+  --> $DIR/forbidden_slices.rs:55:1
    |
 LL | pub static R4: &[u8] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered uninitialized memory, but expected an integer
@@ -144,7 +140,7 @@
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:59:1
+  --> $DIR/forbidden_slices.rs:60:1
    |
 LL | pub static R5: &[u8] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered a pointer, but expected an integer
@@ -157,7 +153,7 @@
    = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:64:1
+  --> $DIR/forbidden_slices.rs:65:1
    |
 LL | pub static R6: &[bool] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered 0x11, but expected a boolean
@@ -168,7 +164,7 @@
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:69:1
+  --> $DIR/forbidden_slices.rs:70:1
    |
 LL | pub static R7: &[u16] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1)
@@ -186,7 +182,7 @@
 note: inside `std::ptr::const_ptr::<impl *const u64>::add`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
 note: inside `R8`
-  --> $DIR/forbidden_slices.rs:76:25
+  --> $DIR/forbidden_slices.rs:77:25
    |
 LL |     from_ptr_range(ptr..ptr.add(1))
    |                         ^^^^^^^^^^
@@ -201,7 +197,7 @@
 note: inside `from_ptr_range::<'_, u32>`
   --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
 note: inside `R9`
-  --> $DIR/forbidden_slices.rs:81:34
+  --> $DIR/forbidden_slices.rs:82:34
    |
 LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) };
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -216,7 +212,7 @@
 note: inside `from_ptr_range::<'_, u32>`
   --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
 note: inside `R10`
-  --> $DIR/forbidden_slices.rs:82:35
+  --> $DIR/forbidden_slices.rs:83:35
    |
 LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) };
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/consts/const-compare-bytes-ub.rs b/tests/ui/consts/const-compare-bytes-ub.rs
index b357bab..903ba15 100644
--- a/tests/ui/consts/const-compare-bytes-ub.rs
+++ b/tests/ui/consts/const-compare-bytes-ub.rs
@@ -7,11 +7,11 @@
 
 fn main() {
     const LHS_NULL: i32 = unsafe {
-        compare_bytes(0 as *const u8, 2 as *const u8, 0)
+        compare_bytes(0 as *const u8, 2 as *const u8, 1)
         //~^ ERROR evaluation of constant value failed
     };
     const RHS_NULL: i32 = unsafe {
-        compare_bytes(1 as *const u8, 0 as *const u8, 0)
+        compare_bytes(1 as *const u8, 0 as *const u8, 1)
         //~^ ERROR evaluation of constant value failed
     };
     const DANGLING_PTR_NON_ZERO_LENGTH: i32 = unsafe {
diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr
index d8971eb..9e49706 100644
--- a/tests/ui/consts/const-compare-bytes-ub.stderr
+++ b/tests/ui/consts/const-compare-bytes-ub.stderr
@@ -1,14 +1,14 @@
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:10:9
    |
-LL |         compare_bytes(0 as *const u8, 2 as *const u8, 0)
+LL |         compare_bytes(0 as *const u8, 2 as *const u8, 1)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:14:9
    |
-LL |         compare_bytes(1 as *const u8, 0 as *const u8, 0)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
+LL |         compare_bytes(1 as *const u8, 0 as *const u8, 1)
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x1[noalloc] is a dangling pointer (it has no provenance)
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:18:9
diff --git a/tests/ui/consts/const-int-unchecked.stderr b/tests/ui/consts/const-int-unchecked.stderr
index 84b2229..3130603 100644
--- a/tests/ui/consts/const-int-unchecked.stderr
+++ b/tests/ui/consts/const-int-unchecked.stderr
@@ -242,19 +242,19 @@
   --> $DIR/const-int-unchecked.rs:123:25
    |
 LL | const _: u16 = unsafe { std::intrinsics::unchecked_add(40000u16, 30000) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_add`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_add`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:126:25
    |
 LL | const _: u32 = unsafe { std::intrinsics::unchecked_sub(14u32, 22) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_sub`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_sub`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:129:25
    |
 LL | const _: u16 = unsafe { std::intrinsics::unchecked_mul(300u16, 250u16) };
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_mul`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_mul`
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-int-unchecked.rs:132:25
diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs
index 94d7bdc..4183dc0 100644
--- a/tests/ui/consts/copy-intrinsic.rs
+++ b/tests/ui/consts/copy-intrinsic.rs
@@ -23,16 +23,20 @@
 const COPY_OOB_1: () = unsafe {
     let mut x = 0i32;
     let dangle = (&mut x as *mut i32).wrapping_add(10);
-    // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs.
-    copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR evaluation of constant value failed [E0080]
-    //~| pointer at offset 40 is out-of-bounds
+    // Zero-sized copy is fine.
+    copy_nonoverlapping(0x100 as *const i32, dangle, 0);
+    // Non-zero-sized copy is not.
+    copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080]
+    //~| 0x100[noalloc] is a dangling pointer
 };
 const COPY_OOB_2: () = unsafe {
     let x = 0i32;
     let dangle = (&x as *const i32).wrapping_add(10);
-    // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs.
-    copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR evaluation of constant value failed [E0080]
-    //~| pointer at offset 40 is out-of-bounds
+    // Zero-sized copy is fine.
+    copy_nonoverlapping(dangle, 0x100 as *mut i32, 0);
+    // Non-zero-sized copy is not.
+    copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080]
+    //~| offset 40 is out-of-bounds
 };
 
 const COPY_SIZE_OVERFLOW: () = unsafe {
diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr
index 0e4e6a6..d34e61c 100644
--- a/tests/ui/consts/copy-intrinsic.stderr
+++ b/tests/ui/consts/copy-intrinsic.stderr
@@ -1,23 +1,23 @@
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:27:5
+  --> $DIR/copy-intrinsic.rs:29:5
    |
-LL |     copy_nonoverlapping(0x100 as *const i32, dangle, 0);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer at offset 40 is out-of-bounds
+LL |     copy_nonoverlapping(0x100 as *const i32, dangle, 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x100[noalloc] is a dangling pointer (it has no provenance)
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:34:5
+  --> $DIR/copy-intrinsic.rs:38:5
    |
-LL |     copy_nonoverlapping(dangle, 0x100 as *mut i32, 0);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC1 has size 4, so pointer at offset 40 is out-of-bounds
+LL |     copy_nonoverlapping(dangle, 0x100 as *mut i32, 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer to 4 bytes starting at offset 40 is out-of-bounds
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:41:5
+  --> $DIR/copy-intrinsic.rs:45:5
    |
 LL |     copy(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy`
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:47:5
+  --> $DIR/copy-intrinsic.rs:51:5
    |
 LL |     copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping`
diff --git a/tests/ui/consts/dangling-alloc-id-ice.rs b/tests/ui/consts/dangling-alloc-id-ice.rs
index 76d6f33..6b07b8b 100644
--- a/tests/ui/consts/dangling-alloc-id-ice.rs
+++ b/tests/ui/consts/dangling-alloc-id-ice.rs
@@ -10,7 +10,7 @@
 }
 
 const FOO: &() = {
-    //~^ ERROR it is undefined behavior to use this value
+    //~^ ERROR encountered dangling pointer
     let y = ();
     unsafe { Foo { y: &y }.long_live_the_unit }
 };
diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr
index 881c0b1..de31acf 100644
--- a/tests/ui/consts/dangling-alloc-id-ice.stderr
+++ b/tests/ui/consts/dangling-alloc-id-ice.stderr
@@ -1,14 +1,8 @@
-error[E0080]: it is undefined behavior to use this value
+error: encountered dangling pointer in final value of constant
   --> $DIR/dangling-alloc-id-ice.rs:12:1
    |
 LL | const FOO: &() = {
-   | ^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free)
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+   | ^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/erroneous_type_in_const_return_value.rs b/tests/ui/consts/erroneous_type_in_const_return_value.rs
new file mode 100644
index 0000000..304211f
--- /dev/null
+++ b/tests/ui/consts/erroneous_type_in_const_return_value.rs
@@ -0,0 +1,12 @@
+//! ICE test #124348
+//! We should not be running const eval if the layout has errors.
+
+enum Eek {
+    TheConst,
+    UnusedByTheConst(Sum),
+    //~^ ERROR cannot find type `Sum` in this scope
+}
+
+const EEK_ZERO: &[Eek] = &[];
+
+fn main() {}
diff --git a/tests/ui/consts/erroneous_type_in_const_return_value.stderr b/tests/ui/consts/erroneous_type_in_const_return_value.stderr
new file mode 100644
index 0000000..453f5b6
--- /dev/null
+++ b/tests/ui/consts/erroneous_type_in_const_return_value.stderr
@@ -0,0 +1,14 @@
+error[E0412]: cannot find type `Sum` in this scope
+  --> $DIR/erroneous_type_in_const_return_value.rs:6:22
+   |
+LL |     UnusedByTheConst(Sum),
+   |                      ^^^ not found in this scope
+   |
+help: consider importing this trait
+   |
+LL + use std::iter::Sum;
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/consts/erroneous_type_in_promoted.rs b/tests/ui/consts/erroneous_type_in_promoted.rs
new file mode 100644
index 0000000..32e33c2
--- /dev/null
+++ b/tests/ui/consts/erroneous_type_in_promoted.rs
@@ -0,0 +1,14 @@
+//! ICE test #124348
+//! We should not be running const eval if the layout has errors.
+
+enum Eek {
+    TheConst,
+    UnusedByTheConst(Sum),
+    //~^ ERROR cannot find type `Sum` in this scope
+}
+
+const fn foo() {
+    let x: &'static [Eek] = &[];
+}
+
+fn main() {}
diff --git a/tests/ui/consts/erroneous_type_in_promoted.stderr b/tests/ui/consts/erroneous_type_in_promoted.stderr
new file mode 100644
index 0000000..0601fc9
--- /dev/null
+++ b/tests/ui/consts/erroneous_type_in_promoted.stderr
@@ -0,0 +1,14 @@
+error[E0412]: cannot find type `Sum` in this scope
+  --> $DIR/erroneous_type_in_promoted.rs:6:22
+   |
+LL |     UnusedByTheConst(Sum),
+   |                      ^^^ not found in this scope
+   |
+help: consider importing this trait
+   |
+LL + use std::iter::Sum;
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs
index e0dd270..57767e9 100644
--- a/tests/ui/consts/offset_from_ub.rs
+++ b/tests/ui/consts/offset_from_ub.rs
@@ -34,8 +34,8 @@
 
 pub const OFFSET_FROM_NULL: isize = {
     let ptr = 0 as *const u8;
-    unsafe { ptr_offset_from(ptr, ptr) } //~ERROR evaluation of constant value failed
-    //~| null pointer is a dangling pointer
+    // Null isn't special for zero-sized "accesses" (i.e., the range between the two pointers)
+    unsafe { ptr_offset_from(ptr, ptr) }
 };
 
 pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC
@@ -67,8 +67,8 @@
     let start_ptr = &4 as *const _ as *const u8;
     let length = 10;
     let end_ptr = (start_ptr).wrapping_add(length);
-    unsafe { ptr_offset_from(end_ptr, end_ptr) } //~ERROR evaluation of constant value failed
-    //~| pointer at offset 10 is out-of-bounds
+    // Out-of-bounds is fine as long as the range between the pointers is empty.
+    unsafe { ptr_offset_from(end_ptr, end_ptr) }
 };
 
 pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr
index e3bac8d..65f75a6 100644
--- a/tests/ui/consts/offset_from_ub.stderr
+++ b/tests/ui/consts/offset_from_ub.stderr
@@ -24,12 +24,6 @@
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/offset_from_ub.rs:37:14
-   |
-LL |     unsafe { ptr_offset_from(ptr, ptr) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
-
-error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:44:14
    |
 LL |     unsafe { ptr_offset_from(ptr2, ptr1) }
@@ -48,12 +42,6 @@
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC1 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/offset_from_ub.rs:70:14
-   |
-LL |     unsafe { ptr_offset_from(end_ptr, end_ptr) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC2 has size 4, so pointer at offset 10 is out-of-bounds
-
-error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:79:14
    |
 LL |     unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }
@@ -109,6 +97,6 @@
 LL |     unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 15 previous errors
+error: aborting due to 13 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/offset_ub.rs b/tests/ui/consts/offset_ub.rs
index 920ecb6..36e4ff1 100644
--- a/tests/ui/consts/offset_ub.rs
+++ b/tests/ui/consts/offset_ub.rs
@@ -17,10 +17,10 @@
 pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE
 pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) }; //~NOTE
 
-// Right now, a zero offset from null is UB
-pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) }; //~NOTE
-
 // Make sure that we don't panic when computing abs(offset*size_of::<T>())
 pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE
 
+// Offset-by-zero is allowed.
+pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
+
 fn main() {}
diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr
index b398b20..89371f0 100644
--- a/tests/ui/consts/offset_ub.stderr
+++ b/tests/ui/consts/offset_ub.stderr
@@ -131,29 +131,16 @@
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
-   |
-note: inside `std::ptr::const_ptr::<impl *const u8>::offset`
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-note: inside `NULL_OFFSET_ZERO`
-  --> $DIR/offset_ub.rs:21:50
-   |
-LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
-   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-   |
    = note: out-of-bounds pointer arithmetic: 0x7f..f[noalloc] is a dangling pointer (it has no provenance)
    |
 note: inside `std::ptr::const_ptr::<impl *const u8>::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
 note: inside `UNDERFLOW_ABS`
-  --> $DIR/offset_ub.rs:24:47
+  --> $DIR/offset_ub.rs:21:47
    |
 LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) };
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 12 previous errors
+error: aborting due to 11 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/extern-flag/empty-extern-arg.stderr b/tests/ui/extern-flag/empty-extern-arg.stderr
index 2785b12..6ad3eff 100644
--- a/tests/ui/extern-flag/empty-extern-arg.stderr
+++ b/tests/ui/extern-flag/empty-extern-arg.stderr
@@ -1,13 +1,6 @@
 error: extern location for std does not exist: 
 
-error: `#[panic_handler]` function required, but not found
-
-error: unwinding panics are not supported without std
-   |
-   = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding
-   = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem
-
 error: requires `sized` lang_item
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs
new file mode 100644
index 0000000..5250e3a
--- /dev/null
+++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs
@@ -0,0 +1,57 @@
+//! Test that coercing between function items of different functions works,
+//! as long as their signatures match. The resulting value is a function pointer.
+
+#![feature(type_alias_impl_trait)]
+
+fn foo<T>(t: T) -> T {
+    t
+}
+
+fn bar<T>(t: T) -> T {
+    t
+}
+
+type F = impl Sized;
+
+fn f(a: F) {
+    let mut x = bar::<F>;
+    x = foo::<()>; //~ ERROR: mismatched types
+    x(a);
+    x(());
+}
+
+type I = impl Sized;
+
+fn i(a: I) {
+    let mut x = bar::<()>;
+    x = foo::<I>; //~ ERROR: mismatched types
+    x(a);
+    x(());
+}
+
+type J = impl Sized;
+
+fn j(a: J) {
+    let x = match true {
+        true => bar::<J>,
+        false => foo::<()>,
+    };
+    x(a);
+    x(());
+}
+
+fn k() -> impl Sized {
+    fn bind<T, F: FnOnce(T) -> T>(_: T, f: F) -> F {
+        f
+    }
+    let x = match true {
+        true => {
+            let f = foo;
+            bind(k(), f)
+        }
+        false => bar::<()>,
+    };
+    todo!()
+}
+
+fn main() {}
diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr
new file mode 100644
index 0000000..0b3331b
--- /dev/null
+++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr
@@ -0,0 +1,38 @@
+error[E0308]: mismatched types
+  --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:18:9
+   |
+LL | type F = impl Sized;
+   |          ---------- the expected opaque type
+...
+LL |     let mut x = bar::<F>;
+   |                 -------- expected due to this value
+LL |     x = foo::<()>;
+   |         ^^^^^^^^^ expected fn item, found a different fn item
+   |
+   = note: expected fn item `fn(F) -> F {bar::<F>}`
+              found fn item `fn(()) {foo::<()>}`
+
+error[E0308]: mismatched types
+  --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:27:9
+   |
+LL | fn foo<T>(t: T) -> T {
+   | -------------------- function `foo` defined here
+...
+LL | type I = impl Sized;
+   |          ---------- the found opaque type
+...
+LL |     let mut x = bar::<()>;
+   |                 --------- expected due to this value
+LL |     x = foo::<I>;
+   |         ^^^^^^^^ expected fn item, found a different fn item
+   |
+   = note: expected fn item `fn(()) {bar::<()>}`
+              found fn item `fn(I) -> I {foo::<I>}`
+help: use parentheses to call this function
+   |
+LL |     x = foo::<I>(/* I */);
+   |                 +++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.rs b/tests/ui/inference/note-and-explain-ReVar-124973.rs
new file mode 100644
index 0000000..f1e2464
--- /dev/null
+++ b/tests/ui/inference/note-and-explain-ReVar-124973.rs
@@ -0,0 +1,8 @@
+//@ edition:2018
+
+#![feature(c_variadic)]
+
+async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {}
+//~^ ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
+
+fn main() {}
diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr
new file mode 100644
index 0000000..574f650
--- /dev/null
+++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr
@@ -0,0 +1,13 @@
+error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
+  --> $DIR/note-and-explain-ReVar-124973.rs:5:73
+   |
+LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {}
+   | ----------------------------------------------------------------------- ^^
+   | |
+   | opaque type defined here
+   |
+   = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/lint/for_loop_over_fallibles.rs b/tests/ui/lint/for_loop_over_fallibles.rs
index 52c3b8f..89ac20c 100644
--- a/tests/ui/lint/for_loop_over_fallibles.rs
+++ b/tests/ui/lint/for_loop_over_fallibles.rs
@@ -41,3 +41,25 @@
 
     Ok(())
 }
+
+fn _by_ref() {
+    // Shared refs
+    for _ in &Some(1) {}
+    //~^ WARN for loop over a `&Option`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+    for _ in &Ok::<_, ()>(1) {}
+    //~^ WARN for loop over a `&Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+
+    // Mutable refs
+    for _ in &mut Some(1) {}
+    //~^ WARN for loop over a `&mut Option`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+    for _ in &mut Ok::<_, ()>(1) {}
+    //~^ WARN for loop over a `&mut Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+}
diff --git a/tests/ui/lint/for_loop_over_fallibles.stderr b/tests/ui/lint/for_loop_over_fallibles.stderr
index 96efdf8..f695de0 100644
--- a/tests/ui/lint/for_loop_over_fallibles.stderr
+++ b/tests/ui/lint/for_loop_over_fallibles.stderr
@@ -97,5 +97,65 @@
 LL |     if let Ok(_) = Ok::<_, ()>([0; 0]) {}
    |     ~~~~~~~~~~ ~~~
 
-warning: 6 warnings emitted
+warning: for loop over a `&Option`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:47:14
+   |
+LL |     for _ in &Some(1) {}
+   |              ^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Some(_) = &Some(1) {}
+   |     ~~~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Some(_) = &Some(1) {}
+   |     ~~~~~~~~~~~~ ~~~
+
+warning: for loop over a `&Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:51:14
+   |
+LL |     for _ in &Ok::<_, ()>(1) {}
+   |              ^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = &Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = &Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: for loop over a `&mut Option`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:57:14
+   |
+LL |     for _ in &mut Some(1) {}
+   |              ^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Some(_) = &mut Some(1) {}
+   |     ~~~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Some(_) = &mut Some(1) {}
+   |     ~~~~~~~~~~~~ ~~~
+
+warning: for loop over a `&mut Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:61:14
+   |
+LL |     for _ in &mut Ok::<_, ()>(1) {}
+   |              ^^^^^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = &mut Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = &mut Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: 10 warnings emitted
 
diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.fixed b/tests/ui/loops/loop-if-else-break-issue-123261.fixed
new file mode 100644
index 0000000..f9e88c1
--- /dev/null
+++ b/tests/ui/loops/loop-if-else-break-issue-123261.fixed
@@ -0,0 +1,31 @@
+//@ run-rustfix
+
+#![allow(unused)]
+
+fn main() {
+    let n = 1;
+    let m = 2;
+    let x = 'block: {
+        if n == 0 {
+            break 'block 1; //~ ERROR [E0268]
+        } else {
+            break 'block 2;
+        }
+    };
+
+    let y = 'block: {
+        if n == 0 {
+            break 'block 1; //~ ERROR [E0268]
+        }
+        break 'block 0;
+    };
+
+    let z = 'block: {
+        if n == 0 {
+            if m > 1 {
+                break 'block 3; //~ ERROR [E0268]
+            }
+        }
+        break 'block 1;
+    };
+}
diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.rs b/tests/ui/loops/loop-if-else-break-issue-123261.rs
new file mode 100644
index 0000000..a1f9dab
--- /dev/null
+++ b/tests/ui/loops/loop-if-else-break-issue-123261.rs
@@ -0,0 +1,31 @@
+//@ run-rustfix
+
+#![allow(unused)]
+
+fn main() {
+    let n = 1;
+    let m = 2;
+    let x = {
+        if n == 0 {
+            break 1; //~ ERROR [E0268]
+        } else {
+            break 2;
+        }
+    };
+
+    let y = {
+        if n == 0 {
+            break 1; //~ ERROR [E0268]
+        }
+        break 0;
+    };
+
+    let z = {
+        if n == 0 {
+            if m > 1 {
+                break 3; //~ ERROR [E0268]
+            }
+        }
+        break 1;
+    };
+}
diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.stderr b/tests/ui/loops/loop-if-else-break-issue-123261.stderr
new file mode 100644
index 0000000..7bd192f
--- /dev/null
+++ b/tests/ui/loops/loop-if-else-break-issue-123261.stderr
@@ -0,0 +1,59 @@
+error[E0268]: `break` outside of a loop or labeled block
+  --> $DIR/loop-if-else-break-issue-123261.rs:10:13
+   |
+LL |             break 1;
+   |             ^^^^^^^ cannot `break` outside of a loop or labeled block
+LL |         } else {
+LL |             break 2;
+   |             ^^^^^^^ cannot `break` outside of a loop or labeled block
+   |
+help: consider labeling this block to be able to break within it
+   |
+LL ~     let x = 'block: {
+LL |         if n == 0 {
+LL ~             break 'block 1;
+LL |         } else {
+LL ~             break 'block 2;
+   |
+
+error[E0268]: `break` outside of a loop or labeled block
+  --> $DIR/loop-if-else-break-issue-123261.rs:18:13
+   |
+LL |             break 1;
+   |             ^^^^^^^ cannot `break` outside of a loop or labeled block
+LL |         }
+LL |         break 0;
+   |         ^^^^^^^ cannot `break` outside of a loop or labeled block
+   |
+help: consider labeling this block to be able to break within it
+   |
+LL ~     let y = 'block: {
+LL |         if n == 0 {
+LL ~             break 'block 1;
+LL |         }
+LL ~         break 'block 0;
+   |
+
+error[E0268]: `break` outside of a loop or labeled block
+  --> $DIR/loop-if-else-break-issue-123261.rs:26:17
+   |
+LL |                 break 3;
+   |                 ^^^^^^^ cannot `break` outside of a loop or labeled block
+...
+LL |         break 1;
+   |         ^^^^^^^ cannot `break` outside of a loop or labeled block
+   |
+help: consider labeling this block to be able to break within it
+   |
+LL ~     let z = 'block: {
+LL |         if n == 0 {
+LL |             if m > 1 {
+LL ~                 break 'block 3;
+LL |             }
+LL |         }
+LL ~         break 'block 1;
+   |
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0268`.
diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs
index de41a07..ff6619c 100644
--- a/tests/ui/parser/bad-let-else-statement.rs
+++ b/tests/ui/parser/bad-let-else-statement.rs
@@ -3,8 +3,7 @@
 #![feature(explicit_tail_calls)]
 
 fn a() {
-    let foo = {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = {
         1
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -22,8 +21,7 @@
 }
 
 fn c() {
-    let foo = if true {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = if true {
         1
     } else {
         0
@@ -43,8 +41,7 @@
 }
 
 fn e() {
-    let foo = match true {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = match true {
         true => 1,
         false => 0
     } else {
@@ -53,10 +50,12 @@
     };
 }
 
-struct X {a: i32}
 fn f() {
-    let foo = X {
-        //~^ WARN irrefutable `let...else` pattern
+    struct X {
+        a: i32,
+    }
+
+    let X { a: 0 } = X {
         a: 1
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -74,8 +73,7 @@
 }
 
 fn h() {
-    let foo = const {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = const {
         1
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -84,8 +82,7 @@
 }
 
 fn i() {
-    let foo = &{
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = &{
         1
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -94,8 +91,8 @@
 }
 
 fn j() {
-    let bar = 0;
-    let foo = bar = { //~ ERROR: cannot assign twice
+    let mut bar = 0;
+    let foo = bar = {
         //~^ WARN irrefutable `let...else` pattern
         1
     } else {
@@ -105,8 +102,7 @@
 }
 
 fn k() {
-    let foo = 1 + {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = 1 + {
         1
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -115,8 +111,8 @@
 }
 
 fn l() {
-    let foo = 1..{
-        //~^ WARN irrefutable `let...else` pattern
+    const RANGE: std::ops::Range<u8> = 0..0;
+    let RANGE = 1..{
         1
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -125,8 +121,7 @@
 }
 
 fn m() {
-    let foo = return {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = return {
         ()
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -135,8 +130,7 @@
 }
 
 fn n() {
-    let foo = -{
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = -{
         1
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -145,8 +139,7 @@
 }
 
 fn o() -> Result<(), ()> {
-    let foo = do yeet {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = do yeet {
         ()
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -155,8 +148,7 @@
 }
 
 fn p() {
-    let foo = become {
-        //~^ WARN irrefutable `let...else` pattern
+    let 0 = become {
         ()
     } else {
         //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
@@ -185,22 +177,37 @@
 
 fn s() {
     macro_rules! a {
-        () => { {} }
-        //~^ WARN irrefutable `let...else` pattern
-        //~| WARN irrefutable `let...else` pattern
+        () => {
+            { 1 }
+        };
     }
 
     macro_rules! b {
         (1) => {
-            let x = a!() else { return; };
+            let 0 = a!() else { return; };
         };
         (2) => {
-            let x = a! {} else { return; };
+            let 0 = a! {} else { return; };
             //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
         };
     }
 
-    b!(1); b!(2);
+    b!(1);
+    b!(2);
+}
+
+fn t() {
+    macro_rules! primitive {
+        (8) => { u8 };
+    }
+
+    let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! {
+        //~^ WARN irrefutable `let...else` pattern
+        8
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
 }
 
 fn main() {}
diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr
index 3f7e176..0bf6a34 100644
--- a/tests/ui/parser/bad-let-else-statement.stderr
+++ b/tests/ui/parser/bad-let-else-statement.stderr
@@ -1,19 +1,18 @@
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:9:5
+  --> $DIR/bad-let-else-statement.rs:8:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = ({
-LL |
+LL ~     let 0 = ({
 LL |         1
 LL ~     }) else {
    |
 
 error: `for...else` loops are not supported
-  --> $DIR/bad-let-else-statement.rs:18:7
+  --> $DIR/bad-let-else-statement.rs:17:7
    |
 LL |       let foo = for i in 1..2 {
    |                 --- `else` is attached to this loop
@@ -28,22 +27,22 @@
    = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:30:5
+  --> $DIR/bad-let-else-statement.rs:28:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = (if true {
-LL |
- ...
+LL ~     let 0 = (if true {
+LL |         1
+LL |     } else {
 LL |         0
 LL ~     }) else {
    |
 
 error: `loop...else` loops are not supported
-  --> $DIR/bad-let-else-statement.rs:39:7
+  --> $DIR/bad-let-else-statement.rs:37:7
    |
 LL |       let foo = loop {
    |                 ---- `else` is attached to this loop
@@ -58,36 +57,34 @@
    = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:50:5
+  --> $DIR/bad-let-else-statement.rs:47:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = (match true {
-LL |
+LL ~     let 0 = (match true {
 LL |         true => 1,
 LL |         false => 0
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:61:5
+  --> $DIR/bad-let-else-statement.rs:60:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = (X {
-LL |
+LL ~     let X { a: 0 } = (X {
 LL |         a: 1
 LL ~     }) else {
    |
 
 error: `while...else` loops are not supported
-  --> $DIR/bad-let-else-statement.rs:70:7
+  --> $DIR/bad-let-else-statement.rs:69:7
    |
 LL |       let foo = while false {
    |                 ----- `else` is attached to this loop
@@ -102,35 +99,33 @@
    = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:80:5
+  --> $DIR/bad-let-else-statement.rs:78:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = (const {
-LL |
+LL ~     let 0 = (const {
 LL |         1
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:90:5
+  --> $DIR/bad-let-else-statement.rs:87:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = &({
-LL |
+LL ~     let 0 = &({
 LL |         1
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:101:5
+  --> $DIR/bad-let-else-statement.rs:98:5
    |
 LL |     } else {
    |     ^
@@ -144,91 +139,85 @@
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:111:5
+  --> $DIR/bad-let-else-statement.rs:107:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = 1 + ({
-LL |
+LL ~     let 0 = 1 + ({
 LL |         1
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:121:5
+  --> $DIR/bad-let-else-statement.rs:117:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = 1..({
-LL |
+LL ~     let RANGE = 1..({
 LL |         1
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:131:5
+  --> $DIR/bad-let-else-statement.rs:126:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = return ({
-LL |
+LL ~     let 0 = return ({
 LL |         ()
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:141:5
+  --> $DIR/bad-let-else-statement.rs:135:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = -({
-LL |
+LL ~     let 0 = -({
 LL |         1
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:151:5
+  --> $DIR/bad-let-else-statement.rs:144:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = do yeet ({
-LL |
+LL ~     let 0 = do yeet ({
 LL |         ()
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:161:5
+  --> $DIR/bad-let-else-statement.rs:153:5
    |
 LL |     } else {
    |     ^
    |
 help: wrap the expression in parentheses
    |
-LL ~     let foo = become ({
-LL |
+LL ~     let 0 = become ({
 LL |         ()
 LL ~     }) else {
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:171:5
+  --> $DIR/bad-let-else-statement.rs:163:5
    |
 LL |     } else {
    |     ^
@@ -242,7 +231,7 @@
    |
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:181:31
+  --> $DIR/bad-let-else-statement.rs:173:31
    |
 LL |     let bad = format_args! {""} else { return; };
    |                               ^
@@ -253,24 +242,38 @@
    |                            ~  ~
 
 error: right curly brace `}` before `else` in a `let...else` statement not allowed
-  --> $DIR/bad-let-else-statement.rs:198:25
+  --> $DIR/bad-let-else-statement.rs:207:5
    |
-LL |             let x = a! {} else { return; };
+LL |     } else {
+   |     ^
+   |
+help: use parentheses instead of braces for this macro
+   |
+LL ~     let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! (
+LL |
+LL |         8
+LL ~     ) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:190:25
+   |
+LL |             let 0 = a! {} else { return; };
    |                         ^
 ...
-LL |     b!(1); b!(2);
-   |            ----- in this macro invocation
+LL |     b!(2);
+   |     ----- in this macro invocation
    |
    = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: use parentheses instead of braces for this macro
    |
-LL |             let x = a! () else { return; };
+LL |             let 0 = a! () else { return; };
    |                        ~~
 
 warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:6:5
+  --> $DIR/bad-let-else-statement.rs:95:5
    |
-LL | /     let foo = {
+LL | /     let foo = bar = {
 LL | |
 LL | |         1
 LL | |     } else {
@@ -281,169 +284,7 @@
    = note: `#[warn(irrefutable_let_patterns)]` on by default
 
 warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:25:5
-   |
-LL | /     let foo = if true {
-LL | |
-LL | |         1
-LL | |     } else {
-LL | |         0
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:46:5
-   |
-LL | /     let foo = match true {
-LL | |
-LL | |         true => 1,
-LL | |         false => 0
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:58:5
-   |
-LL | /     let foo = X {
-LL | |
-LL | |         a: 1
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:77:5
-   |
-LL | /     let foo = const {
-LL | |
-LL | |         1
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:87:5
-   |
-LL | /     let foo = &{
-LL | |
-LL | |         1
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:98:5
-   |
-LL | /     let foo = bar = {
-LL | |
-LL | |         1
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-error[E0384]: cannot assign twice to immutable variable `bar`
-  --> $DIR/bad-let-else-statement.rs:98:15
-   |
-LL |       let bar = 0;
-   |           ---
-   |           |
-   |           first assignment to `bar`
-   |           help: consider making this binding mutable: `mut bar`
-LL |       let foo = bar = {
-   |  _______________^
-LL | |
-LL | |         1
-LL | |     } else {
-   | |_____^ cannot assign twice to immutable variable
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:108:5
-   |
-LL | /     let foo = 1 + {
-LL | |
-LL | |         1
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:118:5
-   |
-LL | /     let foo = 1..{
-LL | |
-LL | |         1
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:128:5
-   |
-LL | /     let foo = return {
-LL | |
-LL | |         ()
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:138:5
-   |
-LL | /     let foo = -{
-LL | |
-LL | |         1
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:148:5
-   |
-LL | /     let foo = do yeet {
-LL | |
-LL | |         ()
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:158:5
-   |
-LL | /     let foo = become {
-LL | |
-LL | |         ()
-LL | |     } else {
-   | |_____^
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:168:5
+  --> $DIR/bad-let-else-statement.rs:160:5
    |
 LL | /     let foo = |x: i32| {
 LL | |
@@ -455,7 +296,7 @@
    = help: consider removing the `else` clause
 
 warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:178:5
+  --> $DIR/bad-let-else-statement.rs:170:5
    |
 LL |     let ok = format_args!("") else { return; };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -464,7 +305,7 @@
    = help: consider removing the `else` clause
 
 warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:181:5
+  --> $DIR/bad-let-else-statement.rs:173:5
    |
 LL |     let bad = format_args! {""} else { return; };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -473,45 +314,16 @@
    = help: consider removing the `else` clause
 
 warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:188:19
+  --> $DIR/bad-let-else-statement.rs:204:5
    |
-LL |           () => { {} }
-   |  ___________________^
+LL | /     let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! {
 LL | |
-LL | |
-LL | |     }
-...  |
-LL | |         (1) => {
-LL | |             let x = a!() else { return; };
-   | |____________^
-...
-LL |       b!(1); b!(2);
-   |       ----- in this macro invocation
+LL | |         8
+LL | |     } else {
+   | |_____^
    |
    = note: this pattern will always match, so the `else` clause is useless
    = help: consider removing the `else` clause
-   = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-warning: irrefutable `let...else` pattern
-  --> $DIR/bad-let-else-statement.rs:188:19
-   |
-LL |           () => { {} }
-   |  ___________________^
-LL | |
-LL | |
-LL | |     }
-...  |
-LL | |         (2) => {
-LL | |             let x = a! {} else { return; };
-   | |____________^
-...
-LL |       b!(1); b!(2);
-   |              ----- in this macro invocation
-   |
-   = note: this pattern will always match, so the `else` clause is useless
-   = help: consider removing the `else` clause
-   = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+error: aborting due to 20 previous errors; 5 warnings emitted
 
-error: aborting due to 20 previous errors; 18 warnings emitted
-
-For more information about this error, try `rustc --explain E0384`.
diff --git a/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr b/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr
index 9407e8a..2f46cb3 100644
--- a/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr
+++ b/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr
@@ -21,16 +21,21 @@
    = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0268]: `break` outside of a loop or labeled block
-  --> $DIR/break-in-unlabeled-block-in-macro.rs:27:19
+  --> $DIR/break-in-unlabeled-block-in-macro.rs:33:17
    |
-LL |         foo!(stmt break ());
-   |                   ^^^^^^^^ cannot `break` outside of a loop or labeled block
+LL |         foo!(=> break ());
+   |                 ^^^^^^^^ cannot `break` outside of a loop or labeled block
+
+error[E0268]: `break` outside of a loop or labeled block
+  --> $DIR/break-in-unlabeled-block-in-macro.rs:38:17
    |
-help: consider labeling this block to be able to break within it
+LL |                 break ()
+   |                 ^^^^^^^^ cannot `break` outside of a loop or labeled block
+...
+LL |         bar!()
+   |         ------ in this macro invocation
    |
-LL ~     'block: {
-LL ~         foo!(stmt break 'block ());
-   |
+   = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0268]: `break` outside of a loop or labeled block
   --> $DIR/break-in-unlabeled-block-in-macro.rs:12:11
@@ -48,21 +53,16 @@
    |         +++++++         ++++++
 
 error[E0268]: `break` outside of a loop or labeled block
-  --> $DIR/break-in-unlabeled-block-in-macro.rs:33:17
+  --> $DIR/break-in-unlabeled-block-in-macro.rs:27:19
    |
-LL |         foo!(=> break ());
-   |                 ^^^^^^^^ cannot `break` outside of a loop or labeled block
-
-error[E0268]: `break` outside of a loop or labeled block
-  --> $DIR/break-in-unlabeled-block-in-macro.rs:38:17
+LL |         foo!(stmt break ());
+   |                   ^^^^^^^^ cannot `break` outside of a loop or labeled block
    |
-LL |                 break ()
-   |                 ^^^^^^^^ cannot `break` outside of a loop or labeled block
-...
-LL |         bar!()
-   |         ------ in this macro invocation
+help: consider labeling this block to be able to break within it
    |
-   = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info)
+LL ~     'block: {
+LL ~         foo!(stmt break 'block ());
+   |
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs
new file mode 100644
index 0000000..ed76815
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs
@@ -0,0 +1,9 @@
+//@ aux-crate:shared=shared.rs
+
+extern crate shared;
+
+pub use shared::Shared;
+
+pub struct SharedInType {
+    pub f: Shared
+}
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs
new file mode 100644
index 0000000..ed76815
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs
@@ -0,0 +1,9 @@
+//@ aux-crate:shared=shared.rs
+
+extern crate shared;
+
+pub use shared::Shared;
+
+pub struct SharedInType {
+    pub f: Shared
+}
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs
new file mode 100644
index 0000000..0e4a73c
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs
@@ -0,0 +1,4 @@
+//@ aux-crate:priv:indirect2=indirect2.rs
+//@ compile-flags: -Zunstable-options
+
+extern crate indirect2;
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs
new file mode 100644
index 0000000..5f6b289
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs
@@ -0,0 +1,4 @@
+//@ aux-crate:shared=shared.rs
+
+// This is public.
+extern crate shared;
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
new file mode 100644
index 0000000..9e2aa89
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
@@ -0,0 +1,22 @@
+//@ force-host
+//@ no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn fn_like(input: TokenStream) -> TokenStream {
+    "".parse().unwrap()
+}
+
+#[proc_macro_derive(PmDerive)]
+pub fn pm_derive(item: TokenStream) -> TokenStream {
+    "".parse().unwrap()
+}
+
+#[proc_macro_attribute]
+pub fn pm_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
+    "".parse().unwrap()
+}
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs
index e7afeb8..4eeecdc 100644
--- a/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs
@@ -1,2 +1,11 @@
 pub struct OtherType;
 pub trait OtherTrait {}
+
+#[macro_export]
+macro_rules! m {
+    () => {};
+}
+
+pub enum E {
+    V1
+}
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs
new file mode 100644
index 0000000..0655e3a
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs
@@ -0,0 +1,5 @@
+//@ aux-crate:shared=shared.rs
+
+extern crate shared;
+
+pub use shared::Shared;
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs
new file mode 100644
index 0000000..efc4daa
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs
@@ -0,0 +1 @@
+pub struct Shared;
diff --git a/tests/ui/privacy/pub-priv-dep/diamond_deps.rs b/tests/ui/privacy/pub-priv-dep/diamond_deps.rs
new file mode 100644
index 0000000..0e1f6f3
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/diamond_deps.rs
@@ -0,0 +1,48 @@
+//@ aux-crate:priv:diamond_priv_dep=diamond_priv_dep.rs
+//@ aux-crate:diamond_pub_dep=diamond_pub_dep.rs
+//@ compile-flags: -Zunstable-options
+
+// A diamond dependency:
+//
+//           diamond_reepxort
+//                  /\
+//        (public) /  \ (PRIVATE)
+//                /    \
+//   diamond_pub_dep  diamond_priv_dep
+//                \    /
+//        (public) \  /  (public)
+//                  \/
+//                shared
+//
+// Where the pub and private crates reexport something from the shared crate.
+//
+// Checks the behavior when the same shared item appears in the public API,
+// depending on whether it comes from the public side or the private side.
+//
+// NOTE: compiletest does not support deduplicating shared dependencies.
+// However, it should work well enough for this test, the only downside is
+// that diamond_shared gets built twice.
+
+#![crate_type = "lib"]
+#![deny(exported_private_dependencies)]
+
+extern crate diamond_priv_dep;
+extern crate diamond_pub_dep;
+
+// FIXME: This should trigger.
+pub fn leaks_priv() -> diamond_priv_dep::Shared {
+    diamond_priv_dep::Shared
+}
+
+pub fn leaks_pub() -> diamond_pub_dep::Shared {
+    diamond_pub_dep::Shared
+}
+
+pub struct PrivInStruct {
+    pub f: diamond_priv_dep::SharedInType
+//~^ ERROR type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface
+}
+
+pub struct PubInStruct {
+    pub f: diamond_pub_dep::SharedInType
+}
diff --git a/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr b/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr
new file mode 100644
index 0000000..8a6d35a
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr
@@ -0,0 +1,14 @@
+error: type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface
+  --> $DIR/diamond_deps.rs:42:5
+   |
+LL |     pub f: diamond_priv_dep::SharedInType
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/diamond_deps.rs:27:9
+   |
+LL | #![deny(exported_private_dependencies)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
index f26dbb4..112eaf5 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
@@ -1,12 +1,20 @@
 //@ aux-crate:priv:priv_dep=priv_dep.rs
 //@ aux-build:pub_dep.rs
+//@ aux-crate:priv:pm=pm.rs
 //@ compile-flags: -Zunstable-options
+
+// Basic behavior check of exported_private_dependencies from either a public
+// dependency or a private one.
+
 #![deny(exported_private_dependencies)]
 
 // This crate is a private dependency
-extern crate priv_dep;
+// FIXME: This should trigger.
+pub extern crate priv_dep;
 // This crate is a public dependency
 extern crate pub_dep;
+// This crate is a private dependency
+extern crate pm;
 
 use priv_dep::{OtherTrait, OtherType};
 use pub_dep::PubType;
@@ -25,7 +33,10 @@
 }
 
 impl PublicType {
-    pub fn pub_fn(param: OtherType) {}
+    pub fn pub_fn_param(param: OtherType) {}
+    //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
+
+    pub fn pub_fn_return() -> OtherType { OtherType }
     //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
 
     fn priv_fn(param: OtherType) {}
@@ -36,9 +47,61 @@
 }
 //~^^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
 
+pub trait WithSuperTrait: OtherTrait {}
+//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
+
+pub trait PubLocalTraitWithAssoc {
+    type X;
+}
+
+pub struct PrivateAssoc;
+impl PubLocalTraitWithAssoc for PrivateAssoc {
+    type X = OtherType;
+//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
+}
+
+pub fn in_bounds<T: OtherTrait>(x: T) { unimplemented!() }
+//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
+
+pub fn private_in_generic() -> std::num::Saturating<OtherType> { unimplemented!() }
+//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
+
+pub static STATIC: OtherType = OtherType;
+//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
+
+pub const CONST: OtherType = OtherType;
+//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
+
+pub type Alias = OtherType;
+//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
+
+pub struct PublicWithPrivateImpl;
+
+// FIXME: This should trigger.
+// See https://github.com/rust-lang/rust/issues/71043
+impl OtherTrait for PublicWithPrivateImpl {}
+
+pub trait PubTraitOnPrivate {}
+
+// FIXME: This should trigger.
+// See https://github.com/rust-lang/rust/issues/71043
+impl PubTraitOnPrivate for OtherType {}
+
 pub struct AllowedPrivType {
     #[allow(exported_private_dependencies)]
     pub allowed: OtherType,
 }
 
+// FIXME: This should trigger.
+pub use priv_dep::m;
+// FIXME: This should trigger.
+pub use pm::fn_like;
+// FIXME: This should trigger.
+pub use pm::PmDerive;
+// FIXME: This should trigger.
+pub use pm::pm_attr;
+
+// FIXME: This should trigger.
+pub use priv_dep::E::V1;
+
 fn main() {}
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
index e62a440..53d461a 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
@@ -1,26 +1,74 @@
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:21:5
+  --> $DIR/pub-priv1.rs:29:5
    |
 LL |     pub field: OtherType,
    |     ^^^^^^^^^^^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/pub-priv1.rs:4:9
+  --> $DIR/pub-priv1.rs:9:9
    |
 LL | #![deny(exported_private_dependencies)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:28:5
+  --> $DIR/pub-priv1.rs:36:5
    |
-LL |     pub fn pub_fn(param: OtherType) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     pub fn pub_fn_param(param: OtherType) {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: type `OtherType` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:39:5
+   |
+LL |     pub fn pub_fn_return() -> OtherType { OtherType }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:35:5
+  --> $DIR/pub-priv1.rs:46:5
    |
 LL |     type Foo: OtherTrait;
    |     ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:50:1
+   |
+LL | pub trait WithSuperTrait: OtherTrait {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: type `OtherType` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:59:5
+   |
+LL |     type X = OtherType;
+   |     ^^^^^^
+
+error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:63:1
+   |
+LL | pub fn in_bounds<T: OtherTrait>(x: T) { unimplemented!() }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: type `OtherType` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:66:1
+   |
+LL | pub fn private_in_generic() -> std::num::Saturating<OtherType> { unimplemented!() }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: type `OtherType` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:69:1
+   |
+LL | pub static STATIC: OtherType = OtherType;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: type `OtherType` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:72:1
+   |
+LL | pub const CONST: OtherType = OtherType;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: type `OtherType` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:75:1
+   |
+LL | pub type Alias = OtherType;
+   | ^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
 
diff --git a/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs b/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs
new file mode 100644
index 0000000..3c6e982
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs
@@ -0,0 +1,15 @@
+//@ aux-crate:priv:reexport=reexport.rs
+//@ compile-flags: -Zunstable-options
+//@ check-pass
+
+// Checks the behavior of a reexported item from a private dependency.
+
+#![crate_type = "lib"]
+#![deny(exported_private_dependencies)]
+
+extern crate reexport;
+
+// FIXME: This should trigger.
+pub fn leaks_priv() -> reexport::Shared {
+    reexport::Shared
+}
diff --git a/tests/ui/privacy/pub-priv-dep/shared_both_private.rs b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs
new file mode 100644
index 0000000..20a4b85
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs
@@ -0,0 +1,32 @@
+//@ aux-crate:priv:shared=shared.rs
+//@ aux-crate:reexport=reexport.rs
+//@ compile-flags: -Zunstable-options
+//@ check-pass
+
+// A shared dependency, where a private dependency reexports a public dependency.
+//
+//         shared_both_private
+//                  /\
+//       (PRIVATE) /  | (PRIVATE)
+//                /   |
+//        reexport    |
+//                \   |
+//        (public) \  /
+//                  \/
+//                shared
+
+#![crate_type = "lib"]
+#![deny(exported_private_dependencies)]
+
+extern crate shared;
+extern crate reexport;
+
+// FIXME: This should trigger.
+pub fn leaks_priv() -> shared::Shared {
+    shared::Shared
+}
+
+// FIXME: This should trigger.
+pub fn leaks_priv_reexport() -> reexport::Shared {
+    reexport::Shared
+}
diff --git a/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs b/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs
new file mode 100644
index 0000000..b329a7a
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs
@@ -0,0 +1,39 @@
+//@ aux-crate:priv:shared=shared.rs
+//@ aux-crate:reexport=reexport.rs
+//@ compile-flags: -Zunstable-options
+//@ check-pass
+
+// A shared dependency, where the public side reexports the same item as a
+// direct private dependency.
+//
+//          shared_direct_private
+//                  /\
+//        (public) /  | (PRIVATE)
+//                /   |
+//        reexport    |
+//                \   |
+//        (public) \  /
+//                  \/
+//                shared
+//
+
+#![crate_type = "lib"]
+#![deny(exported_private_dependencies)]
+
+extern crate shared;
+extern crate reexport;
+
+// FIXME: Should this trigger?
+//
+// One could make an argument that I said I want "reexport" to be public, and
+// since "reexport" says "shared_direct_private" is public, then it should
+// transitively be public for me. However, as written, this is explicitly
+// referring to a dependency that is marked "private", which I think is
+// confusing.
+pub fn leaks_priv() -> shared::Shared {
+    shared::Shared
+}
+
+pub fn leaks_pub() -> reexport::Shared {
+    reexport::Shared
+}
diff --git a/tests/ui/privacy/pub-priv-dep/shared_indirect.rs b/tests/ui/privacy/pub-priv-dep/shared_indirect.rs
new file mode 100644
index 0000000..34b624b
--- /dev/null
+++ b/tests/ui/privacy/pub-priv-dep/shared_indirect.rs
@@ -0,0 +1,29 @@
+//@ aux-crate:priv:shared=shared.rs
+//@ aux-crate:priv:indirect1=indirect1.rs
+//@ compile-flags: -Zunstable-options
+//@ check-pass
+
+// A shared dependency, where it is only indirectly public.
+//
+//            shared_indirect
+//                  /\
+//       (PRIVATE) /  | (PRIVATE)
+//                /   |
+//     indirect1 |    |
+//     (PRIVATE) |    |
+//     indirect2 |    |
+//                \   |
+//        (public) \  /
+//                  \/
+//                shared
+
+#![crate_type = "lib"]
+#![deny(exported_private_dependencies)]
+
+extern crate shared;
+extern crate indirect1;
+
+// FIXME: This should trigger.
+pub fn leaks_priv() -> shared::Shared {
+    shared::Shared
+}
diff --git a/tests/ui/pub/pub-reexport-priv-extern-crate.stderr b/tests/ui/pub/pub-reexport-priv-extern-crate.stderr
index c7fadc6..3fa5b0f 100644
--- a/tests/ui/pub/pub-reexport-priv-extern-crate.stderr
+++ b/tests/ui/pub/pub-reexport-priv-extern-crate.stderr
@@ -22,7 +22,7 @@
 LL |         extern crate core;
    |         ^^^^^^^^^^^^^^^^^^
 
-error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub`
+error[E0365]: extern crate `core` is private, and cannot be re-exported, consider declaring with `pub`
   --> $DIR/pub-reexport-priv-extern-crate.rs:2:9
    |
 LL | pub use core as reexported_core;
@@ -34,4 +34,5 @@
 
 error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0603`.
+Some errors have detailed explanations: E0365, E0603.
+For more information about an error, try `rustc --explain E0365`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr
index aa660db..537819a 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr
@@ -91,7 +91,7 @@
    = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr
    = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
 
-error: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block
   --> $DIR/safe-calls.rs:70:5
    |
 LL |     sse2();
diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs
new file mode 100644
index 0000000..050b7b4
--- /dev/null
+++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs
@@ -0,0 +1,10 @@
+#![feature(pattern_types, core_pattern_type)]
+#![allow(internal_features)]
+
+type Pat<const START: u32, const END: u32> =
+    std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
+//~^ ERROR type and const arguments are not allowed on const parameter `START`
+//~| ERROR type arguments are not allowed on const parameter `END`
+//~| ERROR associated type bindings are not allowed here
+
+fn main() {}
diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr
new file mode 100644
index 0000000..40effe9
--- /dev/null
+++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr
@@ -0,0 +1,38 @@
+error[E0109]: type and const arguments are not allowed on const parameter `START`
+  --> $DIR/bad_const_generics_args_on_const_param.rs:5:44
+   |
+LL |     std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
+   |                                    -----   ^^  ^^^  ^ type and const arguments not allowed
+   |                                    |
+   |                                    not allowed on const parameter `START`
+   |
+note: const parameter `START` defined here
+  --> $DIR/bad_const_generics_args_on_const_param.rs:4:16
+   |
+LL | type Pat<const START: u32, const END: u32> =
+   |                ^^^^^
+
+error[E0109]: type arguments are not allowed on const parameter `END`
+  --> $DIR/bad_const_generics_args_on_const_param.rs:5:64
+   |
+LL |     std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
+   |                                                          ---   ^ type argument not allowed
+   |                                                          |
+   |                                                          not allowed on const parameter `END`
+   |
+note: const parameter `END` defined here
+  --> $DIR/bad_const_generics_args_on_const_param.rs:4:34
+   |
+LL | type Pat<const START: u32, const END: u32> =
+   |                                  ^^^
+
+error[E0229]: associated type bindings are not allowed here
+  --> $DIR/bad_const_generics_args_on_const_param.rs:5:67
+   |
+LL |     std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
+   |                                                                   ^^^^^^^^^^ associated type not allowed here
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0109, E0229.
+For more information about an error, try `rustc --explain E0109`.
diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr
index 4f0f0bc..2abf271 100644
--- a/tests/ui/type/pattern_types/bad_pat.stderr
+++ b/tests/ui/type/pattern_types/bad_pat.stderr
@@ -14,7 +14,7 @@
    |
    = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
 
-error: "wildcard patterns are not permitted for pattern types"
+error: wildcard patterns are not permitted for pattern types
   --> $DIR/bad_pat.rs:11:33
    |
 LL | type Wild = pattern_type!(() is _);
diff --git a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr
index 2eb1754..fe12dd7 100644
--- a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr
+++ b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr
@@ -1,4 +1,4 @@
-warning: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+warning[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:10:5
    |
 LL |     unsf();
@@ -15,3 +15,4 @@
 
 warning: 1 warning emitted
 
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr
index e6d1d4e..05f36ab 100644
--- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr
+++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr
@@ -1,4 +1,4 @@
-warning: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+warning[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/edition_2024_default.rs:13:5
    |
 LL |     unsf();
@@ -15,3 +15,4 @@
 
 warning: 1 warning emitted
 
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr
index 5092c1e..3f97199 100644
--- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr
+++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr
@@ -1,4 +1,4 @@
-error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/in_2024_compatibility.rs:7:5
    |
 LL |     unsf();
@@ -20,3 +20,4 @@
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr
index 4bc604a1..1f80342 100644
--- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr
+++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr
@@ -1,4 +1,4 @@
-error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:9:5
    |
 LL |     unsf();
@@ -17,7 +17,7 @@
 LL | #![deny(unsafe_op_in_unsafe_fn)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
-error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
   --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:11:5
    |
 LL |     *PTR;
@@ -26,7 +26,7 @@
    = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
-error: use of mutable static is unsafe and requires unsafe block (error E0133)
+error[E0133]: use of mutable static is unsafe and requires unsafe block
   --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:13:5
    |
 LL |     VOID = ();
@@ -47,7 +47,7 @@
 LL | #![deny(unused_unsafe)]
    |         ^^^^^^^^^^^^^
 
-error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:24:5
    |
 LL |     unsf();
@@ -67,7 +67,7 @@
    |        ^^^^^^^^
    = note: `#[deny(unsafe_op_in_unsafe_fn)]` implied by `#[deny(warnings)]`
 
-error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
   --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:5
    |
 LL |     *PTR;
@@ -76,7 +76,7 @@
    = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
-error: use of mutable static is unsafe and requires unsafe block (error E0133)
+error[E0133]: use of mutable static is unsafe and requires unsafe block
   --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:28:5
    |
 LL |     VOID = ();
diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr
index b9f5969..1ce22ec 100644
--- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr
+++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr
@@ -1,4 +1,4 @@
-error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:13:5
    |
 LL |     unsf();
@@ -17,7 +17,7 @@
 LL | #![deny(unsafe_op_in_unsafe_fn)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
-error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:17:5
    |
 LL |     unsf();
@@ -26,7 +26,7 @@
    = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
-error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:25:13
    |
 LL |     let y = *x;
@@ -40,7 +40,7 @@
 LL | pub unsafe fn bar(x: *const i32) -> i32 {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:29:9
    |
 LL |     y + *x
@@ -49,7 +49,7 @@
    = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
-error: use of mutable static is unsafe and requires unsafe block (error E0133)
+error[E0133]: use of mutable static is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:38:13
    |
 LL |     let y = BAZ;
@@ -63,7 +63,7 @@
 LL | pub unsafe fn baz() -> i32 {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: use of mutable static is unsafe and requires unsafe block (error E0133)
+error[E0133]: use of mutable static is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:42:9
    |
 LL |     y + BAZ
@@ -72,7 +72,7 @@
    = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
-error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:48:36
    |
 LL | macro_rules! unsafe_macro { () => (unsf()) }
@@ -90,7 +90,7 @@
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `unsafe_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
   --> $DIR/wrapping-unsafe-block-sugg.rs:48:36
    |
 LL | macro_rules! unsafe_macro { () => (unsf()) }
@@ -105,3 +105,4 @@
 
 error: aborting due to 8 previous errors
 
+For more information about this error, try `rustc --explain E0133`.