about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs13
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs21
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs39
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs57
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs40
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs9
-rw-r--r--compiler/rustc_expand/src/base.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs40
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs44
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs3
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs18
-rw-r--r--compiler/rustc_resolve/Cargo.toml1
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs26
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs14
-rw-r--r--compiler/rustc_resolve/src/diagnostics/tests.rs40
-rw-r--r--compiler/rustc_resolve/src/lib.rs2
-rw-r--r--compiler/rustc_resolve/src/macros.rs41
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/codegen.rs91
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs18
-rw-r--r--compiler/rustc_typeck/src/check/intrinsic.rs3
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/macros.rs1
-rw-r--r--library/alloc/src/slice.rs2
-rw-r--r--library/alloc/src/vec/drain.rs2
-rw-r--r--library/alloc/src/vec/in_place_collect.rs2
-rw-r--r--library/alloc/src/vec/in_place_drop.rs2
-rw-r--r--library/alloc/src/vec/into_iter.rs2
-rw-r--r--library/core/src/intrinsics.rs13
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/ptr/const_ptr.rs77
-rw-r--r--library/core/src/ptr/mut_ptr.rs72
-rw-r--r--library/core/src/slice/raw.rs4
m---------library/stdarch0
-rw-r--r--src/test/codegen/intrinsics/offset_from.rs36
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/issue-85848.rs1
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr18
-rw-r--r--src/test/ui/const-generics/issues/issue-86530.rs1
-rw-r--r--src/test/ui/const-generics/issues/issue-86530.stderr18
-rw-r--r--src/test/ui/consts/const-float-bits-conv.rs1
-rw-r--r--src/test/ui/consts/offset_from.rs9
-rw-r--r--src/test/ui/consts/offset_from_ub.rs21
-rw-r--r--src/test/ui/consts/offset_from_ub.stderr14
-rw-r--r--src/test/ui/issues/issue-77919.rs1
-rw-r--r--src/test/ui/issues/issue-77919.stderr16
-rw-r--r--src/test/ui/lint/unused/unused-macro-rules-decl.rs49
-rw-r--r--src/test/ui/lint/unused/unused-macro-rules-decl.stderr26
-rw-r--r--src/test/ui/lint/unused/unused-macro-rules.rs54
-rw-r--r--src/test/ui/lint/unused/unused-macro-rules.stderr34
-rw-r--r--src/test/ui/lint/unused/unused-macros-decl.rs (renamed from src/test/ui/lint/unused/unused-macro.rs)2
-rw-r--r--src/test/ui/lint/unused/unused-macros-decl.stderr (renamed from src/test/ui/lint/unused/unused-macro.stderr)10
-rw-r--r--src/test/ui/lint/unused/unused-macros.rs31
-rw-r--r--src/test/ui/lint/unused/unused-macros.stderr32
-rw-r--r--src/test/ui/macros/issue-41803.rs2
-rw-r--r--src/test/ui/macros/issue-52169.rs1
-rw-r--r--src/test/ui/macros/macro-first-set.rs1
-rw-r--r--src/test/ui/macros/macro-literal.rs1
-rw-r--r--src/test/ui/macros/macro-pub-matcher.rs2
-rw-r--r--src/test/ui/macros/stmt_expr_attr_macro_parse.rs2
-rw-r--r--src/test/ui/macros/type-macros-hlist.rs2
-rw-r--r--src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs1
-rw-r--r--src/test/ui/recursion/issue-83150.rs2
-rw-r--r--src/test/ui/rust-2018/async-ident.fixed2
-rw-r--r--src/test/ui/rust-2018/async-ident.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6252.stderr10
-rw-r--r--src/tools/error_index_generator/main.rs4
69 files changed, 867 insertions, 246 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index d76dfca7960..f7a83373e87 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -713,14 +713,21 @@ fn codegen_regular_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, val);
         };
 
-        ptr_offset_from, (v ptr, v base) {
+        ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) {
             let ty = substs.type_at(0);
             let isize_layout = fx.layout_of(fx.tcx.types.isize);
 
             let pointee_size: u64 = fx.layout_of(ty).size.bytes();
-            let diff = fx.bcx.ins().isub(ptr, base);
+            let diff_bytes = fx.bcx.ins().isub(ptr, base);
             // FIXME this can be an exact division.
-            let val = CValue::by_val(fx.bcx.ins().sdiv_imm(diff, pointee_size as i64), isize_layout);
+            let diff = if intrinsic == sym::ptr_offset_from_unsigned {
+                // Because diff_bytes ULE isize::MAX, this would be fine as signed,
+                // but unsigned is slightly easier to codegen, so might as well.
+                fx.bcx.ins().udiv_imm(diff_bytes, pointee_size as i64)
+            } else {
+                fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64)
+            };
+            let val = CValue::by_val(diff, isize_layout);
             ret.write_cvalue(fx, val);
         };
 
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index cf9cf1b70aa..4407297c943 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -816,6 +816,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
     span: Span,
 ) -> Result<&'ll Value, ()> {
     // macros for error handling:
+    #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
     macro_rules! emit_error {
         ($msg: tt) => {
             emit_error!($msg, )
@@ -1144,6 +1145,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         span: Span,
         args: &[OperandRef<'tcx, &'ll Value>],
     ) -> Result<&'ll Value, ()> {
+        #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
         macro_rules! emit_error {
             ($msg: tt) => {
                 emit_error!($msg, )
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index f15c469ae57..6d6d3ae01f4 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -555,21 +555,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
 
-            sym::ptr_offset_from => {
+            sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
                 let ty = substs.type_at(0);
                 let pointee_size = bx.layout_of(ty).size;
 
-                // This is the same sequence that Clang emits for pointer subtraction.
-                // It can be neither `nsw` nor `nuw` because the input is treated as
-                // unsigned but then the output is treated as signed, so neither works.
                 let a = args[0].immediate();
                 let b = args[1].immediate();
                 let a = bx.ptrtoint(a, bx.type_isize());
                 let b = bx.ptrtoint(b, bx.type_isize());
-                let d = bx.sub(a, b);
                 let pointee_size = bx.const_usize(pointee_size.bytes());
-                // this is where the signed magic happens (notice the `s` in `exactsdiv`)
-                bx.exactsdiv(d, pointee_size)
+                if name == sym::ptr_offset_from {
+                    // This is the same sequence that Clang emits for pointer subtraction.
+                    // It can be neither `nsw` nor `nuw` because the input is treated as
+                    // unsigned but then the output is treated as signed, so neither works.
+                    let d = bx.sub(a, b);
+                    // this is where the signed magic happens (notice the `s` in `exactsdiv`)
+                    bx.exactsdiv(d, pointee_size)
+                } else {
+                    // The `_unsigned` version knows the relative ordering of the pointers,
+                    // so can use `sub nuw` and `udiv exact` instead of dealing in signed.
+                    let d = bx.unchecked_usub(a, b);
+                    bx.exactudiv(d, pointee_size)
+                }
             }
 
             _ => {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 3cc237faf69..59ea40dc2f9 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -308,7 +308,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self);
                 self.write_pointer(offset_ptr, dest)?;
             }
-            sym::ptr_offset_from => {
+            sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
                 let a = self.read_pointer(&args[0])?;
                 let b = self.read_pointer(&args[1])?;
 
@@ -330,8 +330,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         // Both are pointers. They must be into the same allocation.
                         if a_alloc_id != b_alloc_id {
                             throw_ub_format!(
-                                "ptr_offset_from cannot compute offset of pointers into different \
-                                allocations.",
+                                "{} cannot compute offset of pointers into different allocations.",
+                                intrinsic_name,
                             );
                         }
                         // And they must both be valid for zero-sized accesses ("in-bounds or one past the end").
@@ -348,16 +348,39 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             CheckInAllocMsg::OffsetFromTest,
                         )?;
 
+                        if intrinsic_name == sym::ptr_offset_from_unsigned && a_offset < b_offset {
+                            throw_ub_format!(
+                                "{} cannot compute a negative offset, but {} < {}",
+                                intrinsic_name,
+                                a_offset.bytes(),
+                                b_offset.bytes(),
+                            );
+                        }
+
                         // Compute offset.
                         let usize_layout = self.layout_of(self.tcx.types.usize)?;
                         let isize_layout = self.layout_of(self.tcx.types.isize)?;
-                        let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout);
-                        let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout);
-                        let (val, _overflowed, _ty) =
+                        let ret_layout = if intrinsic_name == sym::ptr_offset_from {
+                            isize_layout
+                        } else {
+                            usize_layout
+                        };
+
+                        // The subtraction is always done in `isize` to enforce
+                        // the "no more than `isize::MAX` apart" requirement.
+                        let a_offset = ImmTy::from_uint(a_offset.bytes(), isize_layout);
+                        let b_offset = ImmTy::from_uint(b_offset.bytes(), isize_layout);
+                        let (val, overflowed, _ty) =
                             self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
+                        if overflowed {
+                            throw_ub_format!("Pointers were too far apart for {}", intrinsic_name);
+                        }
+
                         let pointee_layout = self.layout_of(substs.type_at(0))?;
-                        let val = ImmTy::from_scalar(val, isize_layout);
-                        let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
+                        // This re-interprets an isize at ret_layout, but we already checked
+                        // that if ret_layout is usize, then the result must be non-negative.
+                        let val = ImmTy::from_scalar(val, ret_layout);
+                        let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout);
                         self.exact_div(&val, &size, dest)?;
                     }
                 }
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 8926676e815..d627ddb39d0 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -284,8 +284,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Abi::Scalar(s) if force => Some(s.primitive()),
             _ => None,
         };
-        if let Some(_) = scalar_layout {
-            let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
+        if let Some(_s) = scalar_layout {
+            //FIXME(#96185): let size = s.size(self);
+            //FIXME(#96185): assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
+            let size = mplace.layout.size; //FIXME(#96185): remove this line
+            let scalar = alloc.read_scalar(alloc_range(Size::ZERO, size))?;
             return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }));
         }
         let scalar_pair_layout = match mplace.layout.abi {
@@ -302,7 +305,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
             let (a_size, b_size) = (a.size(self), b.size(self));
             let b_offset = a_size.align_to(b.align(self).abi);
-            assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
+            assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
             let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
             let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
             return Ok(Some(ImmTy {
@@ -394,28 +397,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Err(value) => value,
         };
 
-        let field_layout = op.layout.field(self, field);
-        if field_layout.is_zst() {
-            let immediate = Scalar::ZST.into();
-            return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout });
-        }
-        let offset = op.layout.fields.offset(field);
-        let immediate = match *base {
+        let field_layout = base.layout.field(self, field);
+        let offset = base.layout.fields.offset(field);
+        // This makes several assumptions about what layouts we will encounter; we match what
+        // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
+        let field_val: Immediate<_> = match (*base, base.layout.abi) {
+            // the field contains no information
+            _ if field_layout.is_zst() => Scalar::ZST.into(),
             // the field covers the entire type
-            _ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base,
+            _ if field_layout.size == base.layout.size => {
+                assert!(match (base.layout.abi, field_layout.abi) {
+                    (Abi::Scalar(..), Abi::Scalar(..)) => true,
+                    (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
+                    _ => false,
+                });
+                assert!(offset.bytes() == 0);
+                *base
+            }
             // extract fields from types with `ScalarPair` ABI
-            Immediate::ScalarPair(a, b) => {
-                let val = if offset.bytes() == 0 { a } else { b };
-                Immediate::from(val)
+            (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
+                assert!(matches!(field_layout.abi, Abi::Scalar(..)));
+                Immediate::from(if offset.bytes() == 0 {
+                    debug_assert_eq!(field_layout.size, a.size(self));
+                    a_val
+                } else {
+                    debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
+                    debug_assert_eq!(field_layout.size, b.size(self));
+                    b_val
+                })
             }
-            Immediate::Scalar(val) => span_bug!(
+            _ => span_bug!(
                 self.cur_span(),
-                "field access on non aggregate {:#?}, {:#?}",
-                val,
-                op.layout
+                "invalid field access on immediate {}, layout {:#?}",
+                base,
+                base.layout
             ),
         };
-        Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout })
+
+        Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout })
     }
 
     pub fn operand_index(
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index e23c6f2a954..95d6f431391 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -16,7 +16,7 @@ use rustc_target::abi::{HasDataLayout, Size, VariantIdx, Variants};
 use super::{
     alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
     ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy,
-    Operand, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit,
+    Operand, Pointer, Provenance, Scalar, ScalarMaybeUninit,
 };
 
 #[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)]
@@ -700,24 +700,7 @@ where
         src: Immediate<M::PointerTag>,
         dest: &PlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx> {
-        if cfg!(debug_assertions) {
-            // This is a very common path, avoid some checks in release mode
-            assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
-            match src {
-                Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(..))) => assert_eq!(
-                    self.pointer_size(),
-                    dest.layout.size,
-                    "Size mismatch when writing pointer"
-                ),
-                Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Int(int))) => {
-                    assert_eq!(int.size(), dest.layout.size, "Size mismatch when writing bits")
-                }
-                Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // uninit can have any size
-                Immediate::ScalarPair(_, _) => {
-                    // FIXME: Can we check anything here?
-                }
-            }
-        }
+        assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
         trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
 
         // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
@@ -753,31 +736,27 @@ where
         dest: &MPlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx> {
         // Note that it is really important that the type here is the right one, and matches the
-        // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
+        // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
         // to handle padding properly, which is only correct if we never look at this data with the
         // wrong type.
 
-        // Invalid places are a thing: the return place of a diverging function
         let tcx = *self.tcx;
         let Some(mut alloc) = self.get_place_alloc_mut(dest)? else {
             // zero-sized access
             return Ok(());
         };
 
-        // FIXME: We should check that there are dest.layout.size many bytes available in
-        // memory.  The code below is not sufficient, with enough padding it might not
-        // cover all the bytes!
         match value {
             Immediate::Scalar(scalar) => {
-                match dest.layout.abi {
-                    Abi::Scalar(_) => {} // fine
-                    _ => span_bug!(
+                let Abi::Scalar(s) = dest.layout.abi else { span_bug!(
                         self.cur_span(),
                         "write_immediate_to_mplace: invalid Scalar layout: {:#?}",
                         dest.layout
-                    ),
-                }
-                alloc.write_scalar(alloc_range(Size::ZERO, dest.layout.size), scalar)
+                    )
+                };
+                let size = s.size(&tcx);
+                //FIXME(#96185): assert_eq!(dest.layout.size, size, "abi::Scalar size does not match layout size");
+                alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
             }
             Immediate::ScalarPair(a_val, b_val) => {
                 // We checked `ptr_align` above, so all fields will have the alignment they need.
@@ -791,6 +770,7 @@ where
                 };
                 let (a_size, b_size) = (a.size(&tcx), b.size(&tcx));
                 let b_offset = a_size.align_to(b.align(&tcx).abi);
+                assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
 
                 // It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
                 // but that does not work: We could be a newtype around a pair, then the
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 92e3ac04dc4..2dab9ff8986 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -645,17 +645,18 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         // i.e. that we go over the `check_init` below.
         let size = scalar_layout.size(self.ecx);
         let is_full_range = match scalar_layout {
-            ScalarAbi::Initialized { valid_range, .. } => {
+            ScalarAbi::Initialized { .. } => {
                 if M::enforce_number_validity(self.ecx) {
                     false // not "full" since uninit is not accepted
                 } else {
-                    valid_range.is_full_for(size)
+                    scalar_layout.is_always_valid(self.ecx)
                 }
             }
             ScalarAbi::Union { .. } => true,
         };
         if is_full_range {
-            // Nothing to check
+            // Nothing to check. Cruciall we don't even `read_scalar` until here, since that would
+            // fail for `Union` scalars!
             return Ok(());
         }
         // We have something to check: it must at least be initialized.
@@ -688,7 +689,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                     } else {
                         return Ok(());
                     }
-                } else if scalar_layout.valid_range(self.ecx).is_full_for(size) {
+                } else if scalar_layout.is_always_valid(self.ecx) {
                     // Easy. (This is reachable if `enforce_number_validity` is set.)
                     return Ok(());
                 } else {
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 48b5502c20f..9ea09f7d702 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -886,6 +886,8 @@ pub trait ResolverExpand {
         force: bool,
     ) -> Result<Lrc<SyntaxExtension>, Indeterminate>;
 
+    fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize);
+
     fn check_unused_macros(&mut self);
 
     // Resolver interfaces for specific built-in macros.
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 050710097c3..ba0b35470b6 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -156,13 +156,13 @@ impl<'a> ParserAnyMacro<'a> {
 }
 
 struct MacroRulesMacroExpander {
+    node_id: NodeId,
     name: Ident,
     span: Span,
     transparency: Transparency,
     lhses: Vec<Vec<MatcherLoc>>,
     rhses: Vec<mbe::TokenTree>,
     valid: bool,
-    is_local: bool,
 }
 
 impl TTMacroExpander for MacroRulesMacroExpander {
@@ -179,12 +179,12 @@ impl TTMacroExpander for MacroRulesMacroExpander {
             cx,
             sp,
             self.span,
+            self.node_id,
             self.name,
             self.transparency,
             input,
             &self.lhses,
             &self.rhses,
-            self.is_local,
         )
     }
 }
@@ -207,14 +207,17 @@ fn generic_extension<'cx, 'tt>(
     cx: &'cx mut ExtCtxt<'_>,
     sp: Span,
     def_span: Span,
+    node_id: NodeId,
     name: Ident,
     transparency: Transparency,
     arg: TokenStream,
     lhses: &'tt [Vec<MatcherLoc>],
     rhses: &'tt [mbe::TokenTree],
-    is_local: bool,
 ) -> Box<dyn MacResult + 'cx> {
     let sess = &cx.sess.parse_sess;
+    // Macros defined in the current crate have a real node id,
+    // whereas macros from an external crate have a dummy id.
+    let is_local = node_id != DUMMY_NODE_ID;
 
     if cx.trace_macros() {
         let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg));
@@ -296,6 +299,10 @@ fn generic_extension<'cx, 'tt>(
                 let mut p = Parser::new(sess, tts, false, None);
                 p.last_type_ascription = cx.current_expansion.prior_type_ascription;
 
+                if is_local {
+                    cx.resolver.record_macro_rule_usage(node_id, i);
+                }
+
                 // Let the context choose how to interpret the result.
                 // Weird, but useful for X-macros.
                 return Box::new(ParserAnyMacro {
@@ -372,7 +379,7 @@ pub fn compile_declarative_macro(
     features: &Features,
     def: &ast::Item,
     edition: Edition,
-) -> SyntaxExtension {
+) -> (SyntaxExtension, Vec<Span>) {
     debug!("compile_declarative_macro: {:?}", def);
     let mk_syn_ext = |expander| {
         SyntaxExtension::new(
@@ -385,6 +392,7 @@ pub fn compile_declarative_macro(
             &def.attrs,
         )
     };
+    let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new());
 
     let diag = &sess.parse_sess.span_diagnostic;
     let lhs_nm = Ident::new(sym::lhs, def.span);
@@ -445,17 +453,17 @@ pub fn compile_declarative_macro(
             let s = parse_failure_msg(&token);
             let sp = token.span.substitute_dummy(def.span);
             sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
-            return mk_syn_ext(Box::new(macro_rules_dummy_expander));
+            return dummy_syn_ext();
         }
         Error(sp, msg) => {
             sess.parse_sess
                 .span_diagnostic
                 .struct_span_err(sp.substitute_dummy(def.span), &msg)
                 .emit();
-            return mk_syn_ext(Box::new(macro_rules_dummy_expander));
+            return dummy_syn_ext();
         }
         ErrorReported => {
-            return mk_syn_ext(Box::new(macro_rules_dummy_expander));
+            return dummy_syn_ext();
         }
     };
 
@@ -530,6 +538,15 @@ pub fn compile_declarative_macro(
         None => {}
     }
 
+    // Compute the spans of the macro rules
+    // We only take the span of the lhs here,
+    // so that the spans of created warnings are smaller.
+    let rule_spans = if def.id != DUMMY_NODE_ID {
+        lhses.iter().map(|lhs| lhs.span()).collect::<Vec<_>>()
+    } else {
+        Vec::new()
+    };
+
     // Convert the lhses into `MatcherLoc` form, which is better for doing the
     // actual matching. Unless the matcher is invalid.
     let lhses = if valid {
@@ -549,17 +566,16 @@ pub fn compile_declarative_macro(
         vec![]
     };
 
-    mk_syn_ext(Box::new(MacroRulesMacroExpander {
+    let expander = Box::new(MacroRulesMacroExpander {
         name: def.ident,
         span: def.span,
+        node_id: def.id,
         transparency,
         lhses,
         rhses,
         valid,
-        // Macros defined in the current crate have a real node id,
-        // whereas macros from an external crate have a dummy id.
-        is_local: def.id != DUMMY_NODE_ID,
-    }))
+    });
+    (mk_syn_ext(expander), rule_spans)
 }
 
 fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index a965587afb7..0a0f292fe7a 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -303,6 +303,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
         PATH_STATEMENTS,
         UNUSED_ATTRIBUTES,
         UNUSED_MACROS,
+        UNUSED_MACRO_RULES,
         UNUSED_ALLOCATION,
         UNUSED_DOC_COMMENTS,
         UNUSED_EXTERN_CRATES,
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 565ec4e5ab9..7ebb1e85cdb 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -749,6 +749,10 @@ declare_lint! {
 declare_lint! {
     /// The `unused_macros` lint detects macros that were not used.
     ///
+    /// Note that this lint is distinct from the `unused_macro_rules` lint,
+    /// which checks for single rules that never match of an otherwise used
+    /// macro, and thus never expand.
+    ///
     /// ### Example
     ///
     /// ```rust
@@ -776,6 +780,45 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unused_macro_rules` lint detects macro rules that were not used.
+    ///
+    /// Note that the lint is distinct from the `unused_macros` lint, which
+    /// fires if the entire macro is never called, while this lint fires for
+    /// single unused rules of the macro that is otherwise used.
+    /// `unused_macro_rules` fires only if `unused_macros` wouldn't fire.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// macro_rules! unused_empty {
+    ///     (hello) => { println!("Hello, world!") }; // This rule is unused
+    ///     () => { println!("empty") }; // This rule is used
+    /// }
+    ///
+    /// fn main() {
+    ///     unused_empty!(hello);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Unused macro rules may signal a mistake or unfinished code. Furthermore,
+    /// they slow down compilation. Right now, silencing the warning is not
+    /// supported on a single rule level, so you have to add an allow to the
+    /// entire macro definition.
+    ///
+    /// If you intended to export the macro to make it
+    /// available outside of the crate, use the [`macro_export` attribute].
+    ///
+    /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope
+    pub UNUSED_MACRO_RULES,
+    Warn,
+    "detects macro rules that were not used"
+}
+
+declare_lint! {
     /// The `warnings` lint allows you to change the level of other
     /// lints which produce warnings.
     ///
@@ -3104,6 +3147,7 @@ declare_lint_pass! {
         OVERLAPPING_RANGE_ENDPOINTS,
         BINDINGS_WITH_VARIANT_NAME,
         UNUSED_MACROS,
+        UNUSED_MACRO_RULES,
         WARNINGS,
         UNUSED_FEATURES,
         STABLE_FEATURES,
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 350bdfdadb2..ef4f1f5e84e 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -258,6 +258,7 @@ macro_rules! make_mir_visitor {
                 // for best performance, we want to use an iterator rather
                 // than a for-loop, to avoid calling `body::Body::invalidate` for
                 // each basic block.
+                #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
                 macro_rules! basic_blocks {
                     (mut) => (body.basic_blocks_mut().iter_enumerated_mut());
                     () => (body.basic_blocks().iter_enumerated());
@@ -279,6 +280,7 @@ macro_rules! make_mir_visitor {
                     self.visit_local_decl(local, & $($mutability)? body.local_decls[local]);
                 }
 
+                #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
                 macro_rules! type_annotations {
                     (mut) => (body.user_type_annotations.iter_enumerated_mut());
                     () => (body.user_type_annotations.iter_enumerated());
@@ -932,6 +934,7 @@ macro_rules! make_mir_visitor {
                 body: &$($mutability)? Body<'tcx>,
                 location: Location
             ) {
+                #[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
                 macro_rules! basic_blocks {
                     (mut) => (body.basic_blocks_mut());
                     () => (body.basic_blocks());
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 629a550b775..0ef694a3c85 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1145,7 +1145,7 @@ rustc_queries! {
 
     query codegen_fulfill_obligation(
         key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
-    ) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> {
+    ) -> Result<&'tcx ImplSource<'tcx, ()>, traits::CodegenObligationError> {
         cache_on_disk_if { true }
         desc { |tcx|
             "checking if `{}` fulfills its obligations",
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index ffde1294ec6..8c660e38a7f 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -963,3 +963,21 @@ pub enum MethodViolationCode {
     /// the method's receiver (`self` argument) can't be dispatched on
     UndispatchableReceiver,
 }
+
+/// These are the error cases for `codegen_fulfill_obligation`.
+#[derive(Copy, Clone, Debug, Hash, HashStable, Encodable, Decodable)]
+pub enum CodegenObligationError {
+    /// Ambiguity can happen when monomorphizing during trans
+    /// expands to some humongous type that never occurred
+    /// statically -- this humongous type can then overflow,
+    /// leading to an ambiguous result. So report this as an
+    /// overflow bug, since I believe this is the only case
+    /// where ambiguity can result.
+    Ambiguity,
+    /// This can trigger when we probe for the source of a `'static` lifetime requirement
+    /// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
+    /// This can also trigger when we have a global bound that is not actually satisfied,
+    /// but was included during typeck due to the trivial_bounds feature.
+    Unimplemented,
+    FulfillmentError,
+}
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml
index bd27c16c732..b2178ff5995 100644
--- a/compiler/rustc_resolve/Cargo.toml
+++ b/compiler/rustc_resolve/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.0.0"
 edition = "2021"
 
 [lib]
-test = false
 doctest = false
 
 [dependencies]
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 783ff5a3f91..e68d6fdeea5 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -194,7 +194,7 @@ impl<'a> Resolver<'a> {
         }
 
         let ext = Lrc::new(match self.cstore().load_macro_untracked(def_id, &self.session) {
-            LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition),
+            LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition).0,
             LoadedMacro::ProcMacro(ext) => ext,
         });
 
@@ -1218,9 +1218,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
     // Mark the given macro as unused unless its name starts with `_`.
     // Macro uses will remove items from this set, and the remaining
     // items will be reported as `unused_macros`.
-    fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) {
+    fn insert_unused_macro(
+        &mut self,
+        ident: Ident,
+        def_id: LocalDefId,
+        node_id: NodeId,
+        rule_spans: &[Span],
+    ) {
         if !ident.as_str().starts_with('_') {
             self.r.unused_macros.insert(def_id, (node_id, ident));
+            for (rule_i, rule_span) in rule_spans.iter().enumerate() {
+                self.r.unused_macro_rules.insert((def_id, rule_i), (ident, *rule_span));
+            }
         }
     }
 
@@ -1228,15 +1237,16 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         let parent_scope = self.parent_scope;
         let expansion = parent_scope.expansion;
         let def_id = self.r.local_def_id(item.id);
-        let (ext, ident, span, macro_rules) = match &item.kind {
+        let (ext, ident, span, macro_rules, rule_spans) = match &item.kind {
             ItemKind::MacroDef(def) => {
-                let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition()));
-                (ext, item.ident, item.span, def.macro_rules)
+                let (ext, rule_spans) = self.r.compile_macro(item, self.r.session.edition());
+                let ext = Lrc::new(ext);
+                (ext, item.ident, item.span, def.macro_rules, rule_spans)
             }
             ItemKind::Fn(..) => match self.proc_macro_stub(item) {
                 Some((macro_kind, ident, span)) => {
                     self.r.proc_macro_stubs.insert(def_id);
-                    (self.r.dummy_ext(macro_kind), ident, span, false)
+                    (self.r.dummy_ext(macro_kind), ident, span, false, Vec::new())
                 }
                 None => return parent_scope.macro_rules,
             },
@@ -1264,7 +1274,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport));
             } else {
                 self.r.check_reserved_macro_name(ident, res);
-                self.insert_unused_macro(ident, def_id, item.id);
+                self.insert_unused_macro(ident, def_id, item.id, &rule_spans);
             }
             self.r.visibilities.insert(def_id, vis);
             let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
@@ -1287,7 +1297,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 _ => self.resolve_visibility(&item.vis),
             };
             if vis != ty::Visibility::Public {
-                self.insert_unused_macro(ident, def_id, item.id);
+                self.insert_unused_macro(ident, def_id, item.id, &rule_spans);
             }
             self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
             self.r.visibilities.insert(def_id, vis);
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 95e608da012..268e2c9b69b 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -35,6 +35,9 @@ use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, Vis
 use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet};
 use crate::{Segment, UseError};
 
+#[cfg(test)]
+mod tests;
+
 type Res = def::Res<ast::NodeId>;
 
 /// A vector of spans and replacements, a message and applicability.
@@ -2675,3 +2678,14 @@ fn is_span_suitable_for_use_injection(s: Span) -> bool {
     // import or other generated ones
     !s.from_expansion()
 }
+
+/// Convert the given number into the corresponding ordinal
+crate fn ordinalize(v: usize) -> String {
+    let suffix = match ((11..=13).contains(&(v % 100)), v % 10) {
+        (false, 1) => "st",
+        (false, 2) => "nd",
+        (false, 3) => "rd",
+        _ => "th",
+    };
+    format!("{v}{suffix}")
+}
diff --git a/compiler/rustc_resolve/src/diagnostics/tests.rs b/compiler/rustc_resolve/src/diagnostics/tests.rs
new file mode 100644
index 00000000000..2aa6cc61e46
--- /dev/null
+++ b/compiler/rustc_resolve/src/diagnostics/tests.rs
@@ -0,0 +1,40 @@
+use super::ordinalize;
+
+#[test]
+fn test_ordinalize() {
+    assert_eq!(ordinalize(1), "1st");
+    assert_eq!(ordinalize(2), "2nd");
+    assert_eq!(ordinalize(3), "3rd");
+    assert_eq!(ordinalize(4), "4th");
+    assert_eq!(ordinalize(5), "5th");
+    // ...
+    assert_eq!(ordinalize(10), "10th");
+    assert_eq!(ordinalize(11), "11th");
+    assert_eq!(ordinalize(12), "12th");
+    assert_eq!(ordinalize(13), "13th");
+    assert_eq!(ordinalize(14), "14th");
+    // ...
+    assert_eq!(ordinalize(20), "20th");
+    assert_eq!(ordinalize(21), "21st");
+    assert_eq!(ordinalize(22), "22nd");
+    assert_eq!(ordinalize(23), "23rd");
+    assert_eq!(ordinalize(24), "24th");
+    // ...
+    assert_eq!(ordinalize(30), "30th");
+    assert_eq!(ordinalize(31), "31st");
+    assert_eq!(ordinalize(32), "32nd");
+    assert_eq!(ordinalize(33), "33rd");
+    assert_eq!(ordinalize(34), "34th");
+    // ...
+    assert_eq!(ordinalize(7010), "7010th");
+    assert_eq!(ordinalize(7011), "7011th");
+    assert_eq!(ordinalize(7012), "7012th");
+    assert_eq!(ordinalize(7013), "7013th");
+    assert_eq!(ordinalize(7014), "7014th");
+    // ...
+    assert_eq!(ordinalize(7020), "7020th");
+    assert_eq!(ordinalize(7021), "7021st");
+    assert_eq!(ordinalize(7022), "7022nd");
+    assert_eq!(ordinalize(7023), "7023rd");
+    assert_eq!(ordinalize(7024), "7024th");
+}
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 62485beac47..8d3c46c29a8 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -975,6 +975,7 @@ pub struct Resolver<'a> {
     local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>,
     ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>,
     unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>,
+    unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>,
     proc_macro_stubs: FxHashSet<LocalDefId>,
     /// Traces collected during macro resolution and validated when it's complete.
     single_segment_macro_resolutions:
@@ -1374,6 +1375,7 @@ impl<'a> Resolver<'a> {
             potentially_unused_imports: Vec::new(),
             struct_constructors: Default::default(),
             unused_macros: Default::default(),
+            unused_macro_rules: Default::default(),
             proc_macro_stubs: Default::default(),
             single_segment_macro_resolutions: Default::default(),
             multi_segment_macro_resolutions: Default::default(),
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 19a9c1b99fc..2337f72f1e8 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -22,7 +22,8 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind};
 use rustc_hir::def_id::{CrateNum, LocalDefId};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::RegisteredTools;
-use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS};
+use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE};
+use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::feature_err;
 use rustc_session::Session;
@@ -311,6 +312,11 @@ impl<'a> ResolverExpand for Resolver<'a> {
         Ok(ext)
     }
 
+    fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) {
+        let did = self.local_def_id(id);
+        self.unused_macro_rules.remove(&(did, rule_i));
+    }
+
     fn check_unused_macros(&mut self) {
         for (_, &(node_id, ident)) in self.unused_macros.iter() {
             self.lint_buffer.buffer_lint(
@@ -320,6 +326,23 @@ impl<'a> ResolverExpand for Resolver<'a> {
                 &format!("unused macro definition: `{}`", ident.as_str()),
             );
         }
+        for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() {
+            if self.unused_macros.contains_key(&def_id) {
+                // We already lint the entire macro as unused
+                continue;
+            }
+            let node_id = self.def_id_to_node_id[def_id];
+            self.lint_buffer.buffer_lint(
+                UNUSED_MACRO_RULES,
+                node_id,
+                rule_span,
+                &format!(
+                    "{} rule of macro `{}` is never used",
+                    crate::diagnostics::ordinalize(arm_i + 1),
+                    ident.as_str()
+                ),
+            );
+        }
     }
 
     fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool {
@@ -830,10 +853,15 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    /// Compile the macro into a `SyntaxExtension` and possibly replace
-    /// its expander to a pre-defined one for built-in macros.
-    crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension {
-        let mut result = compile_declarative_macro(
+    /// Compile the macro into a `SyntaxExtension` and its rule spans.
+    ///
+    /// Possibly replace its expander to a pre-defined one for built-in macros.
+    crate fn compile_macro(
+        &mut self,
+        item: &ast::Item,
+        edition: Edition,
+    ) -> (SyntaxExtension, Vec<Span>) {
+        let (mut result, mut rule_spans) = compile_declarative_macro(
             &self.session,
             self.session.features_untracked(),
             item,
@@ -849,6 +877,7 @@ impl<'a> Resolver<'a> {
                 match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
                     BuiltinMacroState::NotYetSeen(ext) => {
                         result.kind = ext;
+                        rule_spans = Vec::new();
                         if item.id != ast::DUMMY_NODE_ID {
                             self.builtin_macro_kinds
                                 .insert(self.local_def_id(item.id), result.macro_kind());
@@ -871,6 +900,6 @@ impl<'a> Resolver<'a> {
             }
         }
 
-        result
+        (result, rule_spans)
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index d6885bebc32..2cc6eb03585 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1079,6 +1079,7 @@ symbols! {
         ptr_null,
         ptr_null_mut,
         ptr_offset_from,
+        ptr_offset_from_unsigned,
         pub_macro_rules,
         pub_restricted,
         pure,
diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs
index c76a6542ca1..6ca630b74cc 100644
--- a/compiler/rustc_trait_selection/src/traits/codegen.rs
+++ b/compiler/rustc_trait_selection/src/traits/codegen.rs
@@ -3,13 +3,12 @@
 // seems likely that they should eventually be merged into more
 // general routines.
 
-use crate::infer::{InferCtxt, TyCtxtInferExt};
+use crate::infer::TyCtxtInferExt;
 use crate::traits::{
     FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine,
     Unimplemented,
 };
-use rustc_errors::ErrorGuaranteed;
-use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::traits::CodegenObligationError;
 use rustc_middle::ty::{self, TyCtxt};
 
 /// Attempts to resolve an obligation to an `ImplSource`. The result is
@@ -23,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt};
 pub fn codegen_fulfill_obligation<'tcx>(
     tcx: TyCtxt<'tcx>,
     (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
-) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> {
+) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
     // Remove any references to regions; this helps improve caching.
     let trait_ref = tcx.erase_regions(trait_ref);
     // We expect the input to be fully normalized.
@@ -40,37 +39,8 @@ pub fn codegen_fulfill_obligation<'tcx>(
 
         let selection = match selcx.select(&obligation) {
             Ok(Some(selection)) => selection,
-            Ok(None) => {
-                // Ambiguity can happen when monomorphizing during trans
-                // expands to some humongous type that never occurred
-                // statically -- this humongous type can then overflow,
-                // leading to an ambiguous result. So report this as an
-                // overflow bug, since I believe this is the only case
-                // where ambiguity can result.
-                let reported = infcx.tcx.sess.delay_span_bug(
-                    rustc_span::DUMMY_SP,
-                    &format!(
-                        "encountered ambiguity selecting `{:?}` during codegen, presuming due to \
-                         overflow or prior type error",
-                        trait_ref
-                    ),
-                );
-                return Err(reported);
-            }
-            Err(Unimplemented) => {
-                // This can trigger when we probe for the source of a `'static` lifetime requirement
-                // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
-                // This can also trigger when we have a global bound that is not actually satisfied,
-                // but was included during typeck due to the trivial_bounds feature.
-                let guar = infcx.tcx.sess.delay_span_bug(
-                    rustc_span::DUMMY_SP,
-                    &format!(
-                        "Encountered error `Unimplemented` selecting `{:?}` during codegen",
-                        trait_ref
-                    ),
-                );
-                return Err(guar);
-            }
+            Ok(None) => return Err(CodegenObligationError::Ambiguity),
+            Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented),
             Err(e) => {
                 bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
             }
@@ -85,7 +55,17 @@ pub fn codegen_fulfill_obligation<'tcx>(
         let impl_source = selection.map(|predicate| {
             fulfill_cx.register_predicate_obligation(&infcx, predicate);
         });
-        let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source);
+
+        // In principle, we only need to do this so long as `impl_source`
+        // contains unbound type parameters. It could be a slight
+        // optimization to stop iterating early.
+        let errors = fulfill_cx.select_all_or_error(&infcx);
+        if !errors.is_empty() {
+            return Err(CodegenObligationError::FulfillmentError);
+        }
+
+        let impl_source = infcx.resolve_vars_if_possible(impl_source);
+        let impl_source = infcx.tcx.erase_regions(impl_source);
 
         // Opaque types may have gotten their hidden types constrained, but we can ignore them safely
         // as they will get constrained elsewhere, too.
@@ -95,42 +75,3 @@ pub fn codegen_fulfill_obligation<'tcx>(
         Ok(&*tcx.arena.alloc(impl_source))
     })
 }
-
-// # Global Cache
-
-/// Finishes processes any obligations that remain in the
-/// fulfillment context, and then returns the result with all type
-/// variables removed and regions erased. Because this is intended
-/// for use outside of type inference, if any errors occur,
-/// it will panic. It is used during normalization and other cases
-/// where processing the obligations in `fulfill_cx` may cause
-/// type inference variables that appear in `result` to be
-/// unified, and hence we need to process those obligations to get
-/// the complete picture of the type.
-fn drain_fulfillment_cx_or_panic<'tcx, T>(
-    infcx: &InferCtxt<'_, 'tcx>,
-    fulfill_cx: &mut FulfillmentContext<'tcx>,
-    result: T,
-) -> T
-where
-    T: TypeFoldable<'tcx>,
-{
-    debug!("drain_fulfillment_cx_or_panic()");
-
-    // In principle, we only need to do this so long as `result`
-    // contains unbound type parameters. It could be a slight
-    // optimization to stop iterating early.
-    let errors = fulfill_cx.select_all_or_error(infcx);
-    if !errors.is_empty() {
-        infcx.tcx.sess.delay_span_bug(
-            rustc_span::DUMMY_SP,
-            &format!(
-                "Encountered errors `{:?}` resolving bounds outside of type inference",
-                errors
-            ),
-        );
-    }
-
-    let result = infcx.resolve_vars_if_possible(result);
-    infcx.tcx.erase_regions(result)
-}
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 802a59abe5f..143081d61fb 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -1,6 +1,7 @@
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::traits::CodegenObligationError;
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, Binder, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitor};
 use rustc_span::{sym, DUMMY_SP};
@@ -212,7 +213,22 @@ fn resolve_associated_item<'tcx>(
     let mut bound_vars_collector = BoundVarsCollector::new();
     trait_ref.visit_with(&mut bound_vars_collector);
     let trait_binder = ty::Binder::bind_with_vars(trait_ref, bound_vars_collector.into_vars(tcx));
-    let vtbl = tcx.codegen_fulfill_obligation((param_env, trait_binder))?;
+    let vtbl = match tcx.codegen_fulfill_obligation((param_env, trait_binder)) {
+        Ok(vtbl) => vtbl,
+        Err(CodegenObligationError::Ambiguity) => {
+            let reported = tcx.sess.delay_span_bug(
+                tcx.def_span(trait_item_id),
+                &format!(
+                    "encountered ambiguity selecting `{:?}` during codegen, presuming due to \
+                     overflow or prior type error",
+                    trait_binder
+                ),
+            );
+            return Err(reported);
+        }
+        Err(CodegenObligationError::Unimplemented) => return Ok(None),
+        Err(CodegenObligationError::FulfillmentError) => return Ok(None),
+    };
 
     // Now that we know which impl is being used, we can dispatch to
     // the actual function:
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs
index 0dd8ee88ca2..b67185e5211 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_typeck/src/check/intrinsic.rs
@@ -305,6 +305,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             sym::ptr_offset_from => {
                 (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)
             }
+            sym::ptr_offset_from_unsigned => {
+                (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize)
+            }
             sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
                 (1, vec![param(0), param(0)], param(0))
             }
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index ecebc7ed9ac..fd21b367118 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -127,6 +127,7 @@
 #![feature(pattern)]
 #![feature(ptr_internals)]
 #![feature(ptr_metadata)]
+#![feature(ptr_sub_ptr)]
 #![feature(receiver_trait)]
 #![feature(set_ptr_value)]
 #![feature(slice_group_by)]
diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs
index 22c19243e7f..093b02113c3 100644
--- a/library/alloc/src/macros.rs
+++ b/library/alloc/src/macros.rs
@@ -56,6 +56,7 @@ macro_rules! vec {
 // `slice::into_vec`  function which is only available with cfg(test)
 // NB see the slice::hack module in slice.rs for more information
 #[cfg(all(not(no_global_oom_handling), test))]
+#[cfg_attr(not(bootstrap), allow(unused_macro_rules))]
 macro_rules! vec {
     () => (
         $crate::vec::Vec::new()
diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs
index 5bdf36a63a2..199b3c9d029 100644
--- a/library/alloc/src/slice.rs
+++ b/library/alloc/src/slice.rs
@@ -1056,7 +1056,7 @@ where
         fn drop(&mut self) {
             // `T` is not a zero-sized type, and these are pointers into a slice's elements.
             unsafe {
-                let len = self.end.offset_from(self.start) as usize;
+                let len = self.end.sub_ptr(self.start);
                 ptr::copy_nonoverlapping(self.start, self.dest, len);
             }
         }
diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs
index 1bff19d05c1..5cdee0bd4da 100644
--- a/library/alloc/src/vec/drain.rs
+++ b/library/alloc/src/vec/drain.rs
@@ -163,7 +163,7 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
             // it from the original vec but also avoid creating a &mut to the front since that could
             // invalidate raw pointers to it which some unsafe code might rely on.
             let vec_ptr = vec.as_mut().as_mut_ptr();
-            let drop_offset = drop_ptr.offset_from(vec_ptr) as usize;
+            let drop_offset = drop_ptr.sub_ptr(vec_ptr);
             let to_drop = ptr::slice_from_raw_parts_mut(vec_ptr.add(drop_offset), drop_len);
             ptr::drop_in_place(to_drop);
         }
diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs
index 282af8cc33f..55dcb84ad16 100644
--- a/library/alloc/src/vec/in_place_collect.rs
+++ b/library/alloc/src/vec/in_place_collect.rs
@@ -250,7 +250,7 @@ where
         let sink =
             self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap();
         // iteration succeeded, don't drop head
-        unsafe { ManuallyDrop::new(sink).dst.offset_from(dst_buf) as usize }
+        unsafe { ManuallyDrop::new(sink).dst.sub_ptr(dst_buf) }
     }
 }
 
diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs
index 354d25c2389..1b1ef9130fa 100644
--- a/library/alloc/src/vec/in_place_drop.rs
+++ b/library/alloc/src/vec/in_place_drop.rs
@@ -10,7 +10,7 @@ pub(super) struct InPlaceDrop<T> {
 
 impl<T> InPlaceDrop<T> {
     fn len(&self) -> usize {
-        unsafe { self.dst.offset_from(self.inner) as usize }
+        unsafe { self.dst.sub_ptr(self.inner) }
     }
 }
 
diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs
index a7df6f59b59..9b84a1d9b4b 100644
--- a/library/alloc/src/vec/into_iter.rs
+++ b/library/alloc/src/vec/into_iter.rs
@@ -169,7 +169,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
         let exact = if mem::size_of::<T>() == 0 {
             self.end.addr().wrapping_sub(self.ptr.addr())
         } else {
-            unsafe { self.end.offset_from(self.ptr) as usize }
+            unsafe { self.end.sub_ptr(self.ptr) }
         };
         (exact, Some(exact))
     }
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index d1647c38859..88e4262922d 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1903,6 +1903,11 @@ extern "rust-intrinsic" {
     #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
     pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
 
+    /// See documentation of `<*const T>::sub_ptr` for details.
+    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
+    #[cfg(not(bootstrap))]
+    pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
+
     /// See documentation of `<*const T>::guaranteed_eq` for details.
     ///
     /// Note that, unlike most intrinsics, this is safe to call;
@@ -2385,3 +2390,11 @@ where
 {
     called_in_const.call_once(arg)
 }
+
+/// Bootstrap polyfill
+#[cfg(bootstrap)]
+pub const unsafe fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize {
+    // SAFETY: we have stricter preconditions than `ptr_offset_from`, so can
+    // call it, and its output has to be positive, so we can just cast.
+    unsafe { ptr_offset_from(ptr, base) as _ }
+}
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 1612aa582ad..d1936b6b566 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -126,6 +126,7 @@
 #![feature(const_option)]
 #![feature(const_option_ext)]
 #![feature(const_pin)]
+#![feature(const_ptr_sub_ptr)]
 #![feature(const_replace)]
 #![feature(const_ptr_as_ref)]
 #![feature(const_ptr_is_null)]
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 45964c3a444..028adc796e5 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -611,6 +611,83 @@ impl<T: ?Sized> *const T {
         unsafe { intrinsics::ptr_offset_from(self, origin) }
     }
 
+    /// Calculates the distance between two pointers, *where it's known that
+    /// `self` is equal to or greater than `origin`*. The returned value is in
+    /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+    ///
+    /// This computes the same value that [`offset_from`](#method.offset_from)
+    /// would compute, but with the added precondition that that the offset is
+    /// guaranteed to be non-negative.  This method is equivalent to
+    /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+    /// but it provides slightly more information to the optimizer, which can
+    /// sometimes allow it to optimize slightly better with some backends.
+    ///
+    /// This method can be though of as recovering the `count` that was passed
+    /// to [`add`](#method.add) (or, with the parameters in the other order,
+    /// to [`sub`](#method.sub)).  The following are all equivalent, assuming
+    /// that their safety preconditions are met:
+    /// ```rust
+    /// # #![feature(ptr_sub_ptr)]
+    /// # unsafe fn blah(ptr: *const i32, origin: *const i32, count: usize) -> bool {
+    /// ptr.sub_ptr(origin) == count
+    /// # &&
+    /// origin.add(count) == ptr
+    /// # &&
+    /// ptr.sub(count) == origin
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// - The distance between the pointers must be non-negative (`self >= origin`)
+    ///
+    /// - *All* the safety conditions of [`offset_from`](#method.offset_from)
+    ///   apply to this method as well; see it for the full details.
+    ///
+    /// Importantly, despite the return type of this method being able to represent
+    /// a larger offset, it's still *not permitted* to pass pointers which differ
+    /// by more than `isize::MAX` *bytes*.  As such, the result of this method will
+    /// always be less than or equal to `isize::MAX as usize`.
+    ///
+    /// # Panics
+    ///
+    /// This function panics if `T` is a Zero-Sized Type ("ZST").
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(ptr_sub_ptr)]
+    ///
+    /// let a = [0; 5];
+    /// let ptr1: *const i32 = &a[1];
+    /// let ptr2: *const i32 = &a[3];
+    /// unsafe {
+    ///     assert_eq!(ptr2.sub_ptr(ptr1), 2);
+    ///     assert_eq!(ptr1.add(2), ptr2);
+    ///     assert_eq!(ptr2.sub(2), ptr1);
+    ///     assert_eq!(ptr2.sub_ptr(ptr2), 0);
+    /// }
+    ///
+    /// // This would be incorrect, as the pointers are not correctly ordered:
+    /// // ptr1.offset_from(ptr2)
+    /// ```
+    #[unstable(feature = "ptr_sub_ptr", issue = "95892")]
+    #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]
+    #[inline]
+    pub const unsafe fn sub_ptr(self, origin: *const T) -> usize
+    where
+        T: Sized,
+    {
+        // SAFETY: The comparison has no side-effects, and the intrinsic
+        // does this check internally in the CTFE implementation.
+        unsafe { assert_unsafe_precondition!(self >= origin) };
+
+        let pointee_size = mem::size_of::<T>();
+        assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
+        // SAFETY: the caller must uphold the safety contract for `ptr_offset_from_unsigned`.
+        unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+    }
+
     /// Returns whether two pointers are guaranteed to be equal.
     ///
     /// At runtime this function behaves like `self == other`.
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index ed80cdc9bf9..1a32dd62dfd 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -787,6 +787,78 @@ impl<T: ?Sized> *mut T {
         unsafe { (self as *const T).offset_from(origin) }
     }
 
+    /// Calculates the distance between two pointers, *where it's known that
+    /// `self` is equal to or greater than `origin`*. The returned value is in
+    /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+    ///
+    /// This computes the same value that [`offset_from`](#method.offset_from)
+    /// would compute, but with the added precondition that that the offset is
+    /// guaranteed to be non-negative.  This method is equivalent to
+    /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+    /// but it provides slightly more information to the optimizer, which can
+    /// sometimes allow it to optimize slightly better with some backends.
+    ///
+    /// This method can be though of as recovering the `count` that was passed
+    /// to [`add`](#method.add) (or, with the parameters in the other order,
+    /// to [`sub`](#method.sub)).  The following are all equivalent, assuming
+    /// that their safety preconditions are met:
+    /// ```rust
+    /// # #![feature(ptr_sub_ptr)]
+    /// # unsafe fn blah(ptr: *mut i32, origin: *mut i32, count: usize) -> bool {
+    /// ptr.sub_ptr(origin) == count
+    /// # &&
+    /// origin.add(count) == ptr
+    /// # &&
+    /// ptr.sub(count) == origin
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// - The distance between the pointers must be non-negative (`self >= origin`)
+    ///
+    /// - *All* the safety conditions of [`offset_from`](#method.offset_from)
+    ///   apply to this method as well; see it for the full details.
+    ///
+    /// Importantly, despite the return type of this method being able to represent
+    /// a larger offset, it's still *not permitted* to pass pointers which differ
+    /// by more than `isize::MAX` *bytes*.  As such, the result of this method will
+    /// always be less than or equal to `isize::MAX as usize`.
+    ///
+    /// # Panics
+    ///
+    /// This function panics if `T` is a Zero-Sized Type ("ZST").
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(ptr_sub_ptr)]
+    ///
+    /// let mut a = [0; 5];
+    /// let p: *mut i32 = a.as_mut_ptr();
+    /// unsafe {
+    ///     let ptr1: *mut i32 = p.add(1);
+    ///     let ptr2: *mut i32 = p.add(3);
+    ///
+    ///     assert_eq!(ptr2.sub_ptr(ptr1), 2);
+    ///     assert_eq!(ptr1.add(2), ptr2);
+    ///     assert_eq!(ptr2.sub(2), ptr1);
+    ///     assert_eq!(ptr2.sub_ptr(ptr2), 0);
+    /// }
+    ///
+    /// // This would be incorrect, as the pointers are not correctly ordered:
+    /// // ptr1.offset_from(ptr2)
+    #[unstable(feature = "ptr_sub_ptr", issue = "95892")]
+    #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]
+    #[inline]
+    pub const unsafe fn sub_ptr(self, origin: *const T) -> usize
+    where
+        T: Sized,
+    {
+        // SAFETY: the caller must uphold the safety contract for `sub_ptr`.
+        unsafe { (self as *const T).sub_ptr(origin) }
+    }
+
     /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
     ///
     /// `count` is in units of T; e.g., a `count` of 3 represents a pointer
diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs
index 1b88573035d..6bc60b04b5c 100644
--- a/library/core/src/slice/raw.rs
+++ b/library/core/src/slice/raw.rs
@@ -215,7 +215,7 @@ pub const fn from_mut<T>(s: &mut T) -> &mut [T] {
 #[unstable(feature = "slice_from_ptr_range", issue = "89792")]
 pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
     // SAFETY: the caller must uphold the safety contract for `from_ptr_range`.
-    unsafe { from_raw_parts(range.start, range.end.offset_from(range.start) as usize) }
+    unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
 }
 
 /// Performs the same functionality as [`from_ptr_range`], except that a
@@ -265,5 +265,5 @@ pub unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] {
 #[unstable(feature = "slice_from_ptr_range", issue = "89792")]
 pub unsafe fn from_mut_ptr_range<'a, T>(range: Range<*mut T>) -> &'a mut [T] {
     // SAFETY: the caller must uphold the safety contract for `from_mut_ptr_range`.
-    unsafe { from_raw_parts_mut(range.start, range.end.offset_from(range.start) as usize) }
+    unsafe { from_raw_parts_mut(range.start, range.end.sub_ptr(range.start)) }
 }
diff --git a/library/stdarch b/library/stdarch
-Subproject d215afe9d1cf79c5edb0dfd3cdf4c50aca1f176
+Subproject 28335054b1f417175ab5005cf1d9cf793773793
diff --git a/src/test/codegen/intrinsics/offset_from.rs b/src/test/codegen/intrinsics/offset_from.rs
new file mode 100644
index 00000000000..d0de4c8355d
--- /dev/null
+++ b/src/test/codegen/intrinsics/offset_from.rs
@@ -0,0 +1,36 @@
+// compile-flags: -C opt-level=1
+// only-64bit (because we're using [ui]size)
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+//! Basic optimizations are enabled because otherwise `x86_64-gnu-nopt` had an alloca.
+//! Uses a type with non-power-of-two size to avoid normalizations to shifts.
+
+use std::intrinsics::*;
+
+type RGB = [u8; 3];
+
+// CHECK-LABEL: @offset_from_odd_size
+#[no_mangle]
+pub unsafe fn offset_from_odd_size(a: *const RGB, b: *const RGB) -> isize {
+    // CHECK: start
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: sub i64
+    // CHECK-NEXT: sdiv exact i64 %{{[0-9]+}}, 3
+    // CHECK-NEXT: ret i64
+    ptr_offset_from(a, b)
+}
+
+// CHECK-LABEL: @offset_from_unsigned_odd_size
+#[no_mangle]
+pub unsafe fn offset_from_unsigned_odd_size(a: *const RGB, b: *const RGB) -> usize {
+    // CHECK: start
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: ptrtoint
+    // CHECK-NEXT: sub nuw i64
+    // CHECK-NEXT: udiv exact i64 %{{[0-9]+}}, 3
+    // CHECK-NEXT: ret i64
+    ptr_offset_from_unsigned(a, b)
+}
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs b/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs
index e86ffbf1075..3a7f4c6184c 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-85848.rs
@@ -24,6 +24,7 @@ fn writes_to_path<C>(cap: &C) {
     writes_to_specific_path(&cap);
     //~^ ERROR: the trait bound `(): _Contains<&C>` is not satisfied [E0277]
     //~| ERROR: unconstrained generic constant
+    //~| ERROR: mismatched types [E0308]
 }
 
 fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
index 27f5dce9fb2..d45dfde9a79 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
@@ -18,7 +18,7 @@ note: required because of the requirements on the impl of `Delegates<()>` for `&
 LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {}
    |            ^^^^^^^^^^^^     ^
 note: required by a bound in `writes_to_specific_path`
-  --> $DIR/issue-85848.rs:29:31
+  --> $DIR/issue-85848.rs:30:31
    |
 LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
    |                               ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path`
@@ -43,11 +43,21 @@ note: required because of the requirements on the impl of `Delegates<()>` for `&
 LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {}
    |            ^^^^^^^^^^^^     ^
 note: required by a bound in `writes_to_specific_path`
-  --> $DIR/issue-85848.rs:29:31
+  --> $DIR/issue-85848.rs:30:31
    |
 LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
    |                               ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path`
 
-error: aborting due to 2 previous errors
+error[E0308]: mismatched types
+  --> $DIR/issue-85848.rs:24:5
+   |
+LL |     writes_to_specific_path(&cap);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ expected `true`, found `{ contains::<T, U>() }`
+   |
+   = note: expected type `true`
+              found type `{ contains::<T, U>() }`
+
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/issues/issue-86530.rs b/src/test/ui/const-generics/issues/issue-86530.rs
index b024decd4e1..4a6ffd1f300 100644
--- a/src/test/ui/const-generics/issues/issue-86530.rs
+++ b/src/test/ui/const-generics/issues/issue-86530.rs
@@ -15,6 +15,7 @@ where
 fn unit_literals() {
     z(" ");
     //~^ ERROR: the trait bound `&str: X` is not satisfied
+    //~| ERROR: unconstrained generic constant
 }
 
 fn main() {}
diff --git a/src/test/ui/const-generics/issues/issue-86530.stderr b/src/test/ui/const-generics/issues/issue-86530.stderr
index c63857b2314..c688f838dab 100644
--- a/src/test/ui/const-generics/issues/issue-86530.stderr
+++ b/src/test/ui/const-generics/issues/issue-86530.stderr
@@ -15,6 +15,22 @@ LL | where
 LL |     T: X,
    |        ^ required by this bound in `z`
 
-error: aborting due to previous error
+error: unconstrained generic constant
+  --> $DIR/issue-86530.rs:16:5
+   |
+LL |     z(" ");
+   |     ^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); T::Y]:`
+note: required by a bound in `z`
+  --> $DIR/issue-86530.rs:11:10
+   |
+LL | fn z<T>(t: T)
+   |    - required by a bound in this
+...
+LL |     [(); T::Y]: ,
+   |          ^^^^ required by this bound in `z`
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/consts/const-float-bits-conv.rs b/src/test/ui/consts/const-float-bits-conv.rs
index 310db2174aa..fd5e42ef170 100644
--- a/src/test/ui/consts/const-float-bits-conv.rs
+++ b/src/test/ui/consts/const-float-bits-conv.rs
@@ -3,6 +3,7 @@
 
 #![feature(const_float_bits_conv)]
 #![feature(const_float_classify)]
+#![allow(unused_macro_rules)]
 
 // Don't promote
 const fn nop<T>(x: T) -> T { x }
diff --git a/src/test/ui/consts/offset_from.rs b/src/test/ui/consts/offset_from.rs
index 4c9b1c1571d..b53718316f3 100644
--- a/src/test/ui/consts/offset_from.rs
+++ b/src/test/ui/consts/offset_from.rs
@@ -1,6 +1,8 @@
 // run-pass
 
 #![feature(const_ptr_offset_from)]
+#![feature(const_ptr_sub_ptr)]
+#![feature(ptr_sub_ptr)]
 
 struct Struct {
     field: (),
@@ -43,9 +45,16 @@ pub const OFFSET_EQUAL_INTS: isize = {
     unsafe { ptr.offset_from(ptr) }
 };
 
+pub const OFFSET_UNSIGNED: usize = {
+    let a = ['a', 'b', 'c'];
+    let ptr = a.as_ptr();
+    unsafe { ptr.add(2).sub_ptr(ptr) }
+};
+
 fn main() {
     assert_eq!(OFFSET, 0);
     assert_eq!(OFFSET_2, 1);
     assert_eq!(OVERFLOW, -1);
     assert_eq!(OFFSET_EQUAL_INTS, 0);
+    assert_eq!(OFFSET_UNSIGNED, 2);
 }
diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs
index 939c1e31f9a..f604f57e39d 100644
--- a/src/test/ui/consts/offset_from_ub.rs
+++ b/src/test/ui/consts/offset_from_ub.rs
@@ -1,7 +1,7 @@
 #![feature(const_ptr_offset_from)]
 #![feature(core_intrinsics)]
 
-use std::intrinsics::ptr_offset_from;
+use std::intrinsics::{ptr_offset_from, ptr_offset_from_unsigned};
 
 #[repr(C)]
 struct Struct {
@@ -15,7 +15,7 @@ pub const DIFFERENT_ALLOC: usize = {
     let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
     let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
     let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
-    //~| cannot compute offset of pointers into different allocations.
+    //~| ptr_offset_from cannot compute offset of pointers into different allocations.
     offset as usize
 };
 
@@ -70,4 +70,21 @@ const OUT_OF_BOUNDS_SAME: isize = {
     //~| pointer at offset 10 is out-of-bounds
 };
 
+pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
+    let uninit = std::mem::MaybeUninit::<Struct>::uninit();
+    let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
+    let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
+    let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
+    let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
+    //~| ptr_offset_from_unsigned cannot compute offset of pointers into different allocations.
+    offset as usize
+};
+
+const WRONG_ORDER_UNSIGNED: usize = {
+    let a = ['a', 'b', 'c'];
+    let p = a.as_ptr();
+    unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } //~ERROR evaluation of constant value failed
+    //~| cannot compute a negative offset, but 0 < 8
+};
+
 fn main() {}
diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr
index 293a2b47d30..4c98fd72cac 100644
--- a/src/test/ui/consts/offset_from_ub.stderr
+++ b/src/test/ui/consts/offset_from_ub.stderr
@@ -54,6 +54,18 @@ error[E0080]: evaluation of constant value failed
 LL |     unsafe { ptr_offset_from(end_ptr, end_ptr) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc26 has size 4, so pointer at offset 10 is out-of-bounds
 
-error: aborting due to 8 previous errors
+error[E0080]: evaluation of constant value failed
+  --> $DIR/offset_from_ub.rs:78:27
+   |
+LL |     let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) };
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute offset of pointers into different allocations.
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/offset_from_ub.rs:86:14
+   |
+LL |     unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned cannot compute a negative offset, but 0 < 8
+
+error: aborting due to 10 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/issues/issue-77919.rs b/src/test/ui/issues/issue-77919.rs
index 966d76d148a..1d5d5930731 100644
--- a/src/test/ui/issues/issue-77919.rs
+++ b/src/test/ui/issues/issue-77919.rs
@@ -1,5 +1,6 @@
 fn main() {
     [1; <Multiply<Five, Five>>::VAL];
+    //~^ ERROR: constant expression depends on a generic parameter
 }
 trait TypeVal<T> {
     const VAL: T;
diff --git a/src/test/ui/issues/issue-77919.stderr b/src/test/ui/issues/issue-77919.stderr
index 97bd5ab36b6..c986e47fb55 100644
--- a/src/test/ui/issues/issue-77919.stderr
+++ b/src/test/ui/issues/issue-77919.stderr
@@ -1,5 +1,5 @@
 error[E0412]: cannot find type `PhantomData` in this scope
-  --> $DIR/issue-77919.rs:9:9
+  --> $DIR/issue-77919.rs:10:9
    |
 LL |     _n: PhantomData,
    |         ^^^^^^^^^^^ not found in this scope
@@ -10,7 +10,7 @@ LL | use std::marker::PhantomData;
    |
 
 error[E0412]: cannot find type `VAL` in this scope
-  --> $DIR/issue-77919.rs:11:63
+  --> $DIR/issue-77919.rs:12:63
    |
 LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
    |          -                                                    ^^^ not found in this scope
@@ -18,7 +18,7 @@ LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
    |          help: you might be missing a type parameter: `, VAL`
 
 error[E0046]: not all trait items implemented, missing: `VAL`
-  --> $DIR/issue-77919.rs:11:1
+  --> $DIR/issue-77919.rs:12:1
    |
 LL |     const VAL: T;
    |     ------------- `VAL` from trait
@@ -26,7 +26,15 @@ LL |     const VAL: T;
 LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
 
-error: aborting due to 3 previous errors
+error: constant expression depends on a generic parameter
+  --> $DIR/issue-77919.rs:2:9
+   |
+LL |     [1; <Multiply<Five, Five>>::VAL];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0046, E0412.
 For more information about an error, try `rustc --explain E0046`.
diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.rs b/src/test/ui/lint/unused/unused-macro-rules-decl.rs
new file mode 100644
index 00000000000..537c84940fd
--- /dev/null
+++ b/src/test/ui/lint/unused/unused-macro-rules-decl.rs
@@ -0,0 +1,49 @@
+#![feature(decl_macro)]
+#![deny(unused_macro_rules)]
+// To make sure we are not hitting this
+#![deny(unused_macros)]
+
+// Most simple case
+macro num {
+    (one) => { 1 },
+    (two) => { 2 }, //~ ERROR: 2nd rule of macro
+    (three) => { 3 },
+    (four) => { 4 }, //~ ERROR: 4th rule of macro
+}
+const _NUM: u8 = num!(one) + num!(three);
+
+// Check that allowing the lint works
+#[allow(unused_macro_rules)]
+macro num_allowed {
+    (one) => { 1 },
+    (two) => { 2 },
+    (three) => { 3 },
+    (four) => { 4 },
+}
+const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three);
+
+// Check that macro calls inside the macro trigger as usage
+macro num_rec {
+    (one) => { 1 },
+    (two) => {
+        num_rec!(one) + num_rec!(one)
+    },
+    (three) => { //~ ERROR: 3rd rule of macro
+        num_rec!(one) + num_rec!(two)
+    },
+    (four) => {
+        num_rec!(two) + num_rec!(two)
+    },
+}
+const _NUM_RECURSIVE: u8 = num_rec!(four);
+
+// No error if the macro is public
+pub macro num_public {
+    (one) => { 1 },
+    (two) => { 2 },
+    (three) => { 3 },
+    (four) => { 4 },
+}
+const _NUM_PUBLIC: u8 = num_public!(one) + num_public!(three);
+
+fn main() {}
diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.stderr b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr
new file mode 100644
index 00000000000..4d9b22feda2
--- /dev/null
+++ b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr
@@ -0,0 +1,26 @@
+error: 4th rule of macro `num` is never used
+  --> $DIR/unused-macro-rules-decl.rs:11:5
+   |
+LL |     (four) => { 4 },
+   |     ^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-macro-rules-decl.rs:2:9
+   |
+LL | #![deny(unused_macro_rules)]
+   |         ^^^^^^^^^^^^^^^^^^
+
+error: 2nd rule of macro `num` is never used
+  --> $DIR/unused-macro-rules-decl.rs:9:5
+   |
+LL |     (two) => { 2 },
+   |     ^^^^^
+
+error: 3rd rule of macro `num_rec` is never used
+  --> $DIR/unused-macro-rules-decl.rs:31:5
+   |
+LL |     (three) => {
+   |     ^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/lint/unused/unused-macro-rules.rs b/src/test/ui/lint/unused/unused-macro-rules.rs
index 1a714b8f0a0..eeaf4d1b0a9 100644
--- a/src/test/ui/lint/unused/unused-macro-rules.rs
+++ b/src/test/ui/lint/unused/unused-macro-rules.rs
@@ -1,29 +1,47 @@
+#![deny(unused_macro_rules)]
+// To make sure we are not hitting this
 #![deny(unused_macros)]
 
 // Most simple case
-macro_rules! unused { //~ ERROR: unused macro definition
-    () => {};
+macro_rules! num {
+    (one) => { 1 };
+    (two) => { 2 }; //~ ERROR: 2nd rule of macro
+    (three) => { 3 };
+    (four) => { 4 }; //~ ERROR: 4th rule of macro
 }
+const _NUM: u8 = num!(one) + num!(three);
 
-// Test macros created by macros
-macro_rules! create_macro {
-    () => {
-        macro_rules! m { //~ ERROR: unused macro definition
-            () => {};
-        }
-    };
+// Check that allowing the lint works
+#[allow(unused_macro_rules)]
+macro_rules! num_allowed {
+    (one) => { 1 };
+    (two) => { 2 };
+    (three) => { 3 };
+    (four) => { 4 };
 }
-create_macro!();
+const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three);
 
-#[allow(unused_macros)]
-mod bar {
-    // Test that putting the #[deny] close to the macro's definition
-    // works.
+// Check that macro calls inside the macro trigger as usage
+macro_rules! num_rec {
+    (one) => { 1 };
+    (two) => {
+        num_rec!(one) + num_rec!(one)
+    };
+    (three) => { //~ ERROR: 3rd rule of macro
+        num_rec!(one) + num_rec!(two)
+    };
+    (four) => { num_rec!(two) + num_rec!(two) };
+}
+const _NUM_RECURSIVE: u8 = num_rec!(four);
 
-    #[deny(unused_macros)]
-    macro_rules! unused { //~ ERROR: unused macro definition
-        () => {};
-    }
+// No error if the macro is being exported
+#[macro_export]
+macro_rules! num_exported {
+    (one) => { 1 };
+    (two) => { 2 };
+    (three) => { 3 };
+    (four) => { 4 };
 }
+const _NUM_EXPORTED: u8 = num_exported!(one) + num_exported!(three);
 
 fn main() {}
diff --git a/src/test/ui/lint/unused/unused-macro-rules.stderr b/src/test/ui/lint/unused/unused-macro-rules.stderr
index 59db35b4111..2b3098a5128 100644
--- a/src/test/ui/lint/unused/unused-macro-rules.stderr
+++ b/src/test/ui/lint/unused/unused-macro-rules.stderr
@@ -1,32 +1,26 @@
-error: unused macro definition: `unused`
-  --> $DIR/unused-macro-rules.rs:4:14
+error: 4th rule of macro `num` is never used
+  --> $DIR/unused-macro-rules.rs:10:5
    |
-LL | macro_rules! unused {
-   |              ^^^^^^
+LL |     (four) => { 4 };
+   |     ^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/unused-macro-rules.rs:1:9
    |
-LL | #![deny(unused_macros)]
-   |         ^^^^^^^^^^^^^
+LL | #![deny(unused_macro_rules)]
+   |         ^^^^^^^^^^^^^^^^^^
 
-error: unused macro definition: `m`
-  --> $DIR/unused-macro-rules.rs:11:22
+error: 2nd rule of macro `num` is never used
+  --> $DIR/unused-macro-rules.rs:8:5
    |
-LL |         macro_rules! m {
-   |                      ^
+LL |     (two) => { 2 };
+   |     ^^^^^
 
-error: unused macro definition: `unused`
-  --> $DIR/unused-macro-rules.rs:24:18
+error: 3rd rule of macro `num_rec` is never used
+  --> $DIR/unused-macro-rules.rs:30:5
    |
-LL |     macro_rules! unused {
-   |                  ^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/unused-macro-rules.rs:23:12
-   |
-LL |     #[deny(unused_macros)]
-   |            ^^^^^^^^^^^^^
+LL |     (three) => {
+   |     ^^^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/lint/unused/unused-macro.rs b/src/test/ui/lint/unused/unused-macros-decl.rs
index 302b0727d77..21f6108b18a 100644
--- a/src/test/ui/lint/unused/unused-macro.rs
+++ b/src/test/ui/lint/unused/unused-macros-decl.rs
@@ -1,5 +1,7 @@
 #![feature(decl_macro)]
 #![deny(unused_macros)]
+// To make sure we are not hitting this
+#![deny(unused_macro_rules)]
 
 // Most simple case
 macro unused { //~ ERROR: unused macro definition
diff --git a/src/test/ui/lint/unused/unused-macro.stderr b/src/test/ui/lint/unused/unused-macros-decl.stderr
index 1a73279ed6d..1f426b9d91a 100644
--- a/src/test/ui/lint/unused/unused-macro.stderr
+++ b/src/test/ui/lint/unused/unused-macros-decl.stderr
@@ -1,29 +1,29 @@
 error: unused macro definition: `unused`
-  --> $DIR/unused-macro.rs:5:7
+  --> $DIR/unused-macros-decl.rs:7:7
    |
 LL | macro unused {
    |       ^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/unused-macro.rs:2:9
+  --> $DIR/unused-macros-decl.rs:2:9
    |
 LL | #![deny(unused_macros)]
    |         ^^^^^^^^^^^^^
 
 error: unused macro definition: `unused`
-  --> $DIR/unused-macro.rs:15:11
+  --> $DIR/unused-macros-decl.rs:17:11
    |
 LL |     macro unused {
    |           ^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/unused-macro.rs:14:12
+  --> $DIR/unused-macros-decl.rs:16:12
    |
 LL |     #[deny(unused_macros)]
    |            ^^^^^^^^^^^^^
 
 error: unused macro definition: `unused`
-  --> $DIR/unused-macro.rs:21:22
+  --> $DIR/unused-macros-decl.rs:23:22
    |
 LL |     pub(crate) macro unused {
    |                      ^^^^^^
diff --git a/src/test/ui/lint/unused/unused-macros.rs b/src/test/ui/lint/unused/unused-macros.rs
new file mode 100644
index 00000000000..70b50b2082b
--- /dev/null
+++ b/src/test/ui/lint/unused/unused-macros.rs
@@ -0,0 +1,31 @@
+#![deny(unused_macros)]
+// To make sure we are not hitting this
+#![deny(unused_macro_rules)]
+
+// Most simple case
+macro_rules! unused { //~ ERROR: unused macro definition
+    () => {};
+}
+
+// Test macros created by macros
+macro_rules! create_macro {
+    () => {
+        macro_rules! m { //~ ERROR: unused macro definition
+            () => {};
+        }
+    };
+}
+create_macro!();
+
+#[allow(unused_macros)]
+mod bar {
+    // Test that putting the #[deny] close to the macro's definition
+    // works.
+
+    #[deny(unused_macros)]
+    macro_rules! unused { //~ ERROR: unused macro definition
+        () => {};
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/lint/unused/unused-macros.stderr b/src/test/ui/lint/unused/unused-macros.stderr
new file mode 100644
index 00000000000..d0baf5becec
--- /dev/null
+++ b/src/test/ui/lint/unused/unused-macros.stderr
@@ -0,0 +1,32 @@
+error: unused macro definition: `unused`
+  --> $DIR/unused-macros.rs:6:14
+   |
+LL | macro_rules! unused {
+   |              ^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-macros.rs:1:9
+   |
+LL | #![deny(unused_macros)]
+   |         ^^^^^^^^^^^^^
+
+error: unused macro definition: `m`
+  --> $DIR/unused-macros.rs:13:22
+   |
+LL |         macro_rules! m {
+   |                      ^
+
+error: unused macro definition: `unused`
+  --> $DIR/unused-macros.rs:26:18
+   |
+LL |     macro_rules! unused {
+   |                  ^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-macros.rs:25:12
+   |
+LL |     #[deny(unused_macros)]
+   |            ^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/macros/issue-41803.rs b/src/test/ui/macros/issue-41803.rs
index 19ab81d04d0..bccfdc61146 100644
--- a/src/test/ui/macros/issue-41803.rs
+++ b/src/test/ui/macros/issue-41803.rs
@@ -1,4 +1,6 @@
 // run-pass
+#![allow(unused_macro_rules)]
+
 /// A compile-time map from identifiers to arbitrary (heterogeneous) expressions
 macro_rules! ident_map {
     ( $name:ident = { $($key:ident => $e:expr,)* } ) => {
diff --git a/src/test/ui/macros/issue-52169.rs b/src/test/ui/macros/issue-52169.rs
index 60be97f0aee..f178cd30cb4 100644
--- a/src/test/ui/macros/issue-52169.rs
+++ b/src/test/ui/macros/issue-52169.rs
@@ -1,5 +1,6 @@
 // run-pass
 
+#[allow(unused_macro_rules)]
 macro_rules! a {
     ($i:literal) => { "right" };
     ($i:tt) => { "wrong" };
diff --git a/src/test/ui/macros/macro-first-set.rs b/src/test/ui/macros/macro-first-set.rs
index f85376dabcb..eeb1ddd84ae 100644
--- a/src/test/ui/macros/macro-first-set.rs
+++ b/src/test/ui/macros/macro-first-set.rs
@@ -1,4 +1,5 @@
 // run-pass
+#![allow(unused_macro_rules)]
 
 //{{{ issue 40569 ==============================================================
 
diff --git a/src/test/ui/macros/macro-literal.rs b/src/test/ui/macros/macro-literal.rs
index e08d0a67b43..3c2e71f9c43 100644
--- a/src/test/ui/macros/macro-literal.rs
+++ b/src/test/ui/macros/macro-literal.rs
@@ -21,6 +21,7 @@ macro_rules! only_expr {
     };
 }
 
+#[allow(unused_macro_rules)]
 macro_rules! mtester_dbg {
     ($l:literal) => {
         &format!("macro caught literal: {:?}", $l)
diff --git a/src/test/ui/macros/macro-pub-matcher.rs b/src/test/ui/macros/macro-pub-matcher.rs
index c02e6794edb..174056d6cdf 100644
--- a/src/test/ui/macros/macro-pub-matcher.rs
+++ b/src/test/ui/macros/macro-pub-matcher.rs
@@ -1,5 +1,5 @@
 // run-pass
-#![allow(dead_code, unused_imports)]
+#![allow(dead_code, unused_imports, unused_macro_rules)]
 #![feature(crate_visibility_modifier)]
 
 /**
diff --git a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs
index 3ab50db0ea1..570191d2c90 100644
--- a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs
+++ b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs
@@ -1,4 +1,6 @@
 // run-pass
+#![allow(unused_macro_rules)]
+
 macro_rules! m {
     ($e:expr) => {
         "expr includes attr"
diff --git a/src/test/ui/macros/type-macros-hlist.rs b/src/test/ui/macros/type-macros-hlist.rs
index 77d866cea9c..946b5bd5d93 100644
--- a/src/test/ui/macros/type-macros-hlist.rs
+++ b/src/test/ui/macros/type-macros-hlist.rs
@@ -1,4 +1,6 @@
 // run-pass
+#![allow(unused_macro_rules)]
+
 use std::ops::*;
 
 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
diff --git a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs
index 9d68a7bffde..76c07bbfd81 100644
--- a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs
+++ b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs
@@ -3,6 +3,7 @@
 // Test that failing macro matchers will not cause pre-expansion errors
 // even though they use a feature that is pre-expansion gated.
 
+#[allow(unused_macro_rules)]
 macro_rules! m {
     ($e:expr) => { 0 }; // This fails on the input below due to `, foo`.
     ($e:expr,) => { 1 }; // This also fails to match due to `foo`.
diff --git a/src/test/ui/recursion/issue-83150.rs b/src/test/ui/recursion/issue-83150.rs
index dc25004f89b..aa3f66b2e28 100644
--- a/src/test/ui/recursion/issue-83150.rs
+++ b/src/test/ui/recursion/issue-83150.rs
@@ -1,5 +1,5 @@
 // build-fail
- //~^ overflow evaluating
+//~^ ERROR overflow evaluating the requirement
 
 fn main() {
     let mut iter = 0u8..1;
diff --git a/src/test/ui/rust-2018/async-ident.fixed b/src/test/ui/rust-2018/async-ident.fixed
index f4ae518c71d..e909c79070c 100644
--- a/src/test/ui/rust-2018/async-ident.fixed
+++ b/src/test/ui/rust-2018/async-ident.fixed
@@ -1,4 +1,4 @@
-#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)]
+#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)]
 #![deny(keyword_idents)]
 
 // edition:2015
diff --git a/src/test/ui/rust-2018/async-ident.rs b/src/test/ui/rust-2018/async-ident.rs
index 79c73dafac7..2bfbc3871d1 100644
--- a/src/test/ui/rust-2018/async-ident.rs
+++ b/src/test/ui/rust-2018/async-ident.rs
@@ -1,4 +1,4 @@
-#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)]
+#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)]
 #![deny(keyword_idents)]
 
 // edition:2015
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
index c8239897f3a..d930d486fde 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
@@ -30,7 +30,15 @@ LL |     const VAL: T;
 LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
 
-error: aborting due to 3 previous errors
+error: constant expression depends on a generic parameter
+  --> $DIR/ice-6252.rs:13:9
+   |
+LL |     [1; <Multiply<Five, Five>>::VAL];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0046, E0412.
 For more information about an error, try `rustc --explain E0046`.
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 51ed1b5a61c..1ce02e48c05 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -280,10 +280,6 @@ fn main() {
 fn register_all() -> Vec<(&'static str, Option<&'static str>)> {
     let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();
     macro_rules! register_diagnostics {
-        ($($ecode:ident: $message:expr,)*) => (
-            register_diagnostics!{$($ecode:$message,)* ;}
-        );
-
         ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
             $(
                 {long_codes.extend([