about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_abi/src/lib.rs22
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs11
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/types.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs5
-rw-r--r--compiler/rustc_codegen_cranelift/src/unsize.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs37
-rw-r--r--compiler/rustc_lint/messages.ftl2
-rw-r--r--compiler/rustc_lint/src/types.rs434
-rw-r--r--compiler/rustc_lint/src/types/literal.rs386
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs4
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs4
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs5
-rw-r--r--library/std/src/fs.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs5
-rw-r--r--src/tools/compiletest/src/common.rs3
-rw-r--r--src/tools/compiletest/src/lib.rs3
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs8
-rw-r--r--tests/crashes/130310.rs15
-rw-r--r--tests/run-make/compiler-builtins/rmake.rs4
-rw-r--r--tests/run-make/thumb-none-cortex-m/rmake.rs6
-rw-r--r--tests/ui/const-generics/generic_const_exprs/issue-109141.rs3
-rw-r--r--tests/ui/const-generics/generic_const_exprs/issue-109141.stderr33
-rw-r--r--tests/ui/const-generics/generic_const_exprs/opaque_type.rs1
-rw-r--r--tests/ui/const-generics/generic_const_exprs/opaque_type.stderr14
-rw-r--r--tests/ui/consts/issue-116186.rs2
-rw-r--r--tests/ui/consts/issue-116186.stderr11
-rw-r--r--tests/ui/lint/improper-types-stack-overflow-130310.rs20
-rw-r--r--tests/ui/lint/improper-types-stack-overflow-130310.stderr11
33 files changed, 585 insertions, 520 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 5668d8992c8..7bd2507d1ad 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -833,6 +833,28 @@ pub enum Integer {
 }
 
 impl Integer {
+    pub fn int_ty_str(self) -> &'static str {
+        use Integer::*;
+        match self {
+            I8 => "i8",
+            I16 => "i16",
+            I32 => "i32",
+            I64 => "i64",
+            I128 => "i128",
+        }
+    }
+
+    pub fn uint_ty_str(self) -> &'static str {
+        use Integer::*;
+        match self {
+            I8 => "u8",
+            I16 => "u16",
+            I32 => "u32",
+            I64 => "u64",
+            I128 => "u128",
+        }
+    }
+
     #[inline]
     pub fn size(self) -> Size {
         use Integer::*;
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 4af4b39cc5b..8839829e2f5 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -785,8 +785,10 @@ fn codegen_stmt<'tcx>(
                 }
                 Rvalue::Repeat(ref operand, times) => {
                     let operand = codegen_operand(fx, operand);
-                    let times =
-                        fx.monomorphize(times).eval_target_usize(fx.tcx, ParamEnv::reveal_all());
+                    let times = fx
+                        .monomorphize(times)
+                        .try_to_target_usize(fx.tcx)
+                        .expect("expected monomorphic const in codegen");
                     if operand.layout().size.bytes() == 0 {
                         // Do nothing for ZST's
                     } else if fx.clif_type(operand.layout().ty) == Some(types::I8) {
@@ -944,7 +946,10 @@ fn codegen_stmt<'tcx>(
 fn codegen_array_len<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, place: CPlace<'tcx>) -> Value {
     match *place.layout().ty.kind() {
         ty::Array(_elem_ty, len) => {
-            let len = fx.monomorphize(len).eval_target_usize(fx.tcx, ParamEnv::reveal_all()) as i64;
+            let len = fx
+                .monomorphize(len)
+                .try_to_target_usize(fx.tcx)
+                .expect("expected monomorphic const in codegen") as i64;
             fx.bcx.ins().iconst(fx.pointer_type, len)
         }
         ty::Slice(_elem_ty) => place.to_ptr_unsized().1,
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs
index 7baf0a3868d..8a55a23128d 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs
@@ -44,7 +44,7 @@ impl DebugContext {
                 type_dbg,
                 ty,
                 *elem_ty,
-                len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()),
+                len.try_to_target_usize(tcx).expect("expected monomorphic const in codegen"),
             ),
             // ty::Slice(_) | ty::Str
             // ty::Dynamic
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index a5621aec244..b96abb14b2e 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -131,9 +131,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
 
             let idx = generic_args[2]
                 .expect_const()
-                .eval(fx.tcx, ty::ParamEnv::reveal_all(), span)
-                .unwrap()
-                .1
+                .try_to_valtree()
+                .expect("expected monomorphic const in codegen")
                 .unwrap_branch();
 
             assert_eq!(x.layout(), y.layout());
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index e09cd16e89a..8cfe93b4d9c 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -24,10 +24,10 @@ pub(crate) fn unsized_info<'tcx>(
     let (source, target) =
         fx.tcx.struct_lockstep_tails_for_codegen(source, target, ParamEnv::reveal_all());
     match (&source.kind(), &target.kind()) {
-        (&ty::Array(_, len), &ty::Slice(_)) => fx
-            .bcx
-            .ins()
-            .iconst(fx.pointer_type, len.eval_target_usize(fx.tcx, ParamEnv::reveal_all()) as i64),
+        (&ty::Array(_, len), &ty::Slice(_)) => fx.bcx.ins().iconst(
+            fx.pointer_type,
+            len.try_to_target_usize(fx.tcx).expect("expected monomorphic const in codegen") as i64,
+        ),
         (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind))
             if src_dyn_kind == target_dyn_kind =>
         {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 6c84a40defb..57e396415cc 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -125,7 +125,9 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>(
 
     let (size, align) = cx.size_and_align_of(array_type);
 
-    let upper_bound = len.eval_target_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong;
+    let upper_bound = len
+        .try_to_target_usize(cx.tcx)
+        .expect("expected monomorphic const in codegen") as c_longlong;
 
     let subrange =
         unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) };
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index f6b45eb4466..3d1007a4673 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -115,9 +115,9 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     let (source, target) =
         cx.tcx().struct_lockstep_tails_for_codegen(source, target, bx.param_env());
     match (source.kind(), target.kind()) {
-        (&ty::Array(_, len), &ty::Slice(_)) => {
-            cx.const_usize(len.eval_target_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
-        }
+        (&ty::Array(_, len), &ty::Slice(_)) => cx.const_usize(
+            len.try_to_target_usize(cx.tcx()).expect("expected monomorphic const in codegen"),
+        ),
         (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind))
             if src_dyn_kind == target_dyn_kind =>
         {
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 55a71db9bae..369ab387bea 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -188,7 +188,8 @@ fn push_debuginfo_type_name<'tcx>(
                     _ => write!(
                         output,
                         ",{}>",
-                        len.eval_target_usize(tcx, ty::ParamEnv::reveal_all())
+                        len.try_to_target_usize(tcx)
+                            .expect("expected monomorphic const in codegen")
                     )
                     .unwrap(),
                 }
@@ -200,7 +201,8 @@ fn push_debuginfo_type_name<'tcx>(
                     _ => write!(
                         output,
                         "; {}]",
-                        len.eval_target_usize(tcx, ty::ParamEnv::reveal_all())
+                        len.try_to_target_usize(tcx)
+                            .expect("expected monomorphic const in codegen")
                     )
                     .unwrap(),
                 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 37474d85b11..6bf75293fce 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -114,7 +114,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
                 let count = self
                     .monomorphize(count)
-                    .eval_target_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all());
+                    .try_to_target_usize(bx.tcx())
+                    .expect("expected monomorphic const in codegen");
 
                 bx.write_operand_repeatedly(cg_elem, count, dest);
             }
@@ -803,7 +804,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         if let Some(index) = place.as_local() {
             if let LocalRef::Operand(op) = self.locals[index] {
                 if let ty::Array(_, n) = op.layout.ty.kind() {
-                    let n = n.eval_target_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all());
+                    let n = n
+                        .try_to_target_usize(bx.tcx())
+                        .expect("expected monomorphic const in codegen");
                     return bx.cx().const_usize(n);
                 }
             }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 45a6efc7a6a..7dd6deb4fe6 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -11,7 +11,6 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
 use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind};
 use rustc_infer::infer;
-use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
@@ -2413,17 +2412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         len: ty::Const<'tcx>,
         min_len: u64,
     ) -> (Option<Ty<'tcx>>, Ty<'tcx>) {
-        let len = match len.eval(self.tcx, self.param_env, span) {
-            Ok((_, val)) => val
-                .try_to_scalar()
-                .and_then(|scalar| scalar.try_to_scalar_int().ok())
-                .map(|int| int.to_target_usize(self.tcx)),
-            Err(ErrorHandled::Reported(..)) => {
-                let guar = self.error_scrutinee_unfixed_length(span);
-                return (Some(Ty::new_error(self.tcx, guar)), arr_ty);
-            }
-            Err(ErrorHandled::TooGeneric(..)) => None,
-        };
+        let len = len.try_eval_target_usize(self.tcx, self.param_env);
 
         let guar = if let Some(len) = len {
             // Now we know the length...
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index c2555d2bb47..2fbbc3200e1 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -803,7 +803,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
         // We must deeply normalize in the new solver, since later lints
         // expect that types that show up in the typeck are fully
         // normalized.
-        let value = if self.should_normalize {
+        let mut value = if self.should_normalize {
             let body_id = tcx.hir().body_owner_def_id(self.body.id());
             let cause = ObligationCause::misc(self.span.to_span(tcx), body_id);
             let at = self.fcx.at(&cause, self.fcx.param_env);
@@ -818,12 +818,27 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
             value
         };
 
+        // Bail if there are any non-region infer.
         if value.has_non_region_infer() {
             let guar = self.report_error(value);
-            new_err(tcx, guar)
-        } else {
-            tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased)
+            value = new_err(tcx, guar);
+        }
+
+        // Erase the regions from the ty, since it's not really meaningful what
+        // these region values are; there's not a trivial correspondence between
+        // regions in the HIR and MIR, so when we turn the body into MIR, there's
+        // no reason to keep regions around. They will be repopulated during MIR
+        // borrowck, and specifically region constraints will be populated during
+        // MIR typeck which is run on the new body.
+        value = tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased);
+
+        // Normalize consts in writeback, because GCE doesn't normalize eagerly.
+        if tcx.features().generic_const_exprs {
+            value =
+                value.fold_with(&mut EagerlyNormalizeConsts { tcx, param_env: self.fcx.param_env });
         }
+
+        value
     }
 }
 
@@ -858,3 +873,17 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
         predicate
     }
 }
+
+struct EagerlyNormalizeConsts<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerlyNormalizeConsts<'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        self.tcx.try_normalize_erasing_regions(self.param_env, ct).unwrap_or(ct)
+    }
+}
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 0e3d34355a1..e71c5676ce4 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -395,6 +395,8 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent
 lint_improper_ctypes_pat_help = consider using the base type instead
 
 lint_improper_ctypes_pat_reason = pattern types have no C equivalent
+
+lint_improper_ctypes_recursion_limit_reached = type is infinitely recursive
 lint_improper_ctypes_slice_help = consider using a raw pointer instead
 
 lint_improper_ctypes_slice_reason = slices have no C equivalent
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 900c496e033..f9d0cd49708 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -3,9 +3,9 @@ use std::ops::ControlFlow;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::DiagMessage;
-use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
+use rustc_hir::{Expr, ExprKind};
 use rustc_middle::bug;
-use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
+use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
 use rustc_middle::ty::{
     self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
 };
@@ -13,22 +13,23 @@ use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::sym;
 use rustc_span::{source_map, Span, Symbol};
-use rustc_target::abi::{Abi, Integer, Size, TagEncoding, Variants, WrappingRange};
+use rustc_target::abi::{Abi, TagEncoding, Variants, WrappingRange};
 use rustc_target::spec::abi::Abi as SpecAbi;
 use tracing::debug;
-use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::lints::{
     AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
     AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
     AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
-    InvalidNanComparisonsSuggestion, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
-    OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp,
-    OverflowingLiteral, OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons,
-    UseInclusiveRange, VariantSizeDifferencesDiag,
+    InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag,
 };
 use crate::{fluent_generated as fluent, LateContext, LateLintPass, LintContext};
 
+mod literal;
+
+use literal::{int_ty_range, lint_literal, uint_ty_range};
+
 declare_lint! {
     /// The `unused_comparisons` lint detects comparisons made useless by
     /// limits of the types involved.
@@ -185,403 +186,6 @@ impl TypeLimits {
     }
 }
 
-/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`).
-/// Returns `true` iff the lint was emitted.
-fn lint_overflowing_range_endpoint<'tcx>(
-    cx: &LateContext<'tcx>,
-    lit: &hir::Lit,
-    lit_val: u128,
-    max: u128,
-    expr: &'tcx hir::Expr<'tcx>,
-    ty: &str,
-) -> bool {
-    // Look past casts to support cases like `0..256 as u8`
-    let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id)
-        && let ExprKind::Cast(_, _) = par_expr.kind
-    {
-        (par_expr, expr.span)
-    } else {
-        (expr, expr.span)
-    };
-
-    // We only want to handle exclusive (`..`) ranges,
-    // which are represented as `ExprKind::Struct`.
-    let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false };
-    let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false };
-    if !is_range_literal(struct_expr) {
-        return false;
-    };
-    let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false };
-
-    // We can suggest using an inclusive range
-    // (`..=`) instead only if it is the `end` that is
-    // overflowing and only by 1.
-    if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) {
-        return false;
-    };
-
-    use rustc_ast::{LitIntType, LitKind};
-    let suffix = match lit.node {
-        LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
-        LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
-        LitKind::Int(_, LitIntType::Unsuffixed) => "",
-        _ => bug!(),
-    };
-
-    let sub_sugg = if expr.span.lo() == lit_span.lo() {
-        let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false };
-        UseInclusiveRange::WithoutParen {
-            sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
-            start,
-            literal: lit_val - 1,
-            suffix,
-        }
-    } else {
-        UseInclusiveRange::WithParen {
-            eq_sugg: expr.span.shrink_to_lo(),
-            lit_sugg: lit_span,
-            literal: lit_val - 1,
-            suffix,
-        }
-    };
-
-    cx.emit_span_lint(
-        OVERFLOWING_LITERALS,
-        struct_expr.span,
-        RangeEndpointOutOfRange { ty, sub: sub_sugg },
-    );
-
-    // We've just emitted a lint, special cased for `(...)..MAX+1` ranges,
-    // return `true` so the callers don't also emit a lint
-    true
-}
-
-// For `isize` & `usize`, be conservative with the warnings, so that the
-// warnings are consistent between 32- and 64-bit platforms.
-fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) {
-    match int_ty {
-        ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()),
-        ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()),
-        ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()),
-        ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()),
-        ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()),
-        ty::IntTy::I128 => (i128::MIN, i128::MAX),
-    }
-}
-
-fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) {
-    let max = match uint_ty {
-        ty::UintTy::Usize => u64::MAX.into(),
-        ty::UintTy::U8 => u8::MAX.into(),
-        ty::UintTy::U16 => u16::MAX.into(),
-        ty::UintTy::U32 => u32::MAX.into(),
-        ty::UintTy::U64 => u64::MAX.into(),
-        ty::UintTy::U128 => u128::MAX,
-    };
-    (0, max)
-}
-
-fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> {
-    let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?;
-    let firstch = src.chars().next()?;
-
-    if firstch == '0' {
-        match src.chars().nth(1) {
-            Some('x' | 'b') => return Some(src),
-            _ => return None,
-        }
-    }
-
-    None
-}
-
-fn report_bin_hex_error(
-    cx: &LateContext<'_>,
-    expr: &hir::Expr<'_>,
-    ty: attr::IntType,
-    size: Size,
-    repr_str: String,
-    val: u128,
-    negative: bool,
-) {
-    let (t, actually) = match ty {
-        attr::IntType::SignedInt(t) => {
-            let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) };
-            (t.name_str(), actually.to_string())
-        }
-        attr::IntType::UnsignedInt(t) => {
-            let actually = size.truncate(val);
-            (t.name_str(), actually.to_string())
-        }
-    };
-    let sign =
-        if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
-    let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
-        |suggestion_ty| {
-            if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
-                let (sans_suffix, _) = repr_str.split_at(pos);
-                OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
-            } else {
-                OverflowingBinHexSub::Help { suggestion_ty }
-            }
-        },
-    );
-    let sign_bit_sub = (!negative)
-        .then(|| {
-            let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else {
-                return None;
-            };
-
-            let Some(bit_width) = int_ty.bit_width() else {
-                return None; // isize case
-            };
-
-            // Skip if sign bit is not set
-            if (val & (1 << (bit_width - 1))) == 0 {
-                return None;
-            }
-
-            let lit_no_suffix =
-                if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
-                    repr_str.split_at(pos).0
-                } else {
-                    &repr_str
-                };
-
-            Some(OverflowingBinHexSignBitSub {
-                span: expr.span,
-                lit_no_suffix,
-                negative_val: actually.clone(),
-                int_ty: int_ty.name_str(),
-                uint_ty: int_ty.to_unsigned().name_str(),
-            })
-        })
-        .flatten();
-
-    cx.emit_span_lint(
-        OVERFLOWING_LITERALS,
-        expr.span,
-        OverflowingBinHex {
-            ty: t,
-            lit: repr_str.clone(),
-            dec: val,
-            actually,
-            sign,
-            sub,
-            sign_bit_sub,
-        },
-    )
-}
-
-// This function finds the next fitting type and generates a suggestion string.
-// It searches for fitting types in the following way (`X < Y`):
-//  - `iX`: if literal fits in `uX` => `uX`, else => `iY`
-//  - `-iX` => `iY`
-//  - `uX` => `uY`
-//
-// No suggestion for: `isize`, `usize`.
-fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> {
-    use ty::IntTy::*;
-    use ty::UintTy::*;
-    macro_rules! find_fit {
-        ($ty:expr, $val:expr, $negative:expr,
-         $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => {
-            {
-                let _neg = if negative { 1 } else { 0 };
-                match $ty {
-                    $($type => {
-                        $(if !negative && val <= uint_ty_range($utypes).1 {
-                            return Some($utypes.name_str())
-                        })*
-                        $(if val <= int_ty_range($itypes).1 as u128 + _neg {
-                            return Some($itypes.name_str())
-                        })*
-                        None
-                    },)+
-                    _ => None
-                }
-            }
-        }
-    }
-    match t.kind() {
-        ty::Int(i) => find_fit!(i, val, negative,
-                      I8 => [U8] => [I16, I32, I64, I128],
-                      I16 => [U16] => [I32, I64, I128],
-                      I32 => [U32] => [I64, I128],
-                      I64 => [U64] => [I128],
-                      I128 => [U128] => []),
-        ty::Uint(u) => find_fit!(u, val, negative,
-                      U8 => [U8, U16, U32, U64, U128] => [],
-                      U16 => [U16, U32, U64, U128] => [],
-                      U32 => [U32, U64, U128] => [],
-                      U64 => [U64, U128] => [],
-                      U128 => [U128] => []),
-        _ => None,
-    }
-}
-
-fn lint_int_literal<'tcx>(
-    cx: &LateContext<'tcx>,
-    type_limits: &TypeLimits,
-    e: &'tcx hir::Expr<'tcx>,
-    lit: &hir::Lit,
-    t: ty::IntTy,
-    v: u128,
-) {
-    let int_type = t.normalize(cx.sess().target.pointer_width);
-    let (min, max) = int_ty_range(int_type);
-    let max = max as u128;
-    let negative = type_limits.negated_expr_id == Some(e.hir_id);
-
-    // Detect literal value out of range [min, max] inclusive
-    // avoiding use of -min to prevent overflow/panic
-    if (negative && v > max + 1) || (!negative && v > max) {
-        if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
-            report_bin_hex_error(
-                cx,
-                e,
-                attr::IntType::SignedInt(ty::ast_int_ty(t)),
-                Integer::from_int_ty(cx, t).size(),
-                repr_str,
-                v,
-                negative,
-            );
-            return;
-        }
-
-        if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) {
-            // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
-            return;
-        }
-
-        let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span };
-        let lit = cx
-            .sess()
-            .source_map()
-            .span_to_snippet(span)
-            .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() });
-        let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
-            .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
-
-        cx.emit_span_lint(
-            OVERFLOWING_LITERALS,
-            span,
-            OverflowingInt { ty: t.name_str(), lit, min, max, help },
-        );
-    }
-}
-
-fn lint_uint_literal<'tcx>(
-    cx: &LateContext<'tcx>,
-    e: &'tcx hir::Expr<'tcx>,
-    lit: &hir::Lit,
-    t: ty::UintTy,
-) {
-    let uint_type = t.normalize(cx.sess().target.pointer_width);
-    let (min, max) = uint_ty_range(uint_type);
-    let lit_val: u128 = match lit.node {
-        // _v is u8, within range by definition
-        ast::LitKind::Byte(_v) => return,
-        ast::LitKind::Int(v, _) => v.get(),
-        _ => bug!(),
-    };
-
-    if lit_val < min || lit_val > max {
-        if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) {
-            match par_e.kind {
-                hir::ExprKind::Cast(..) => {
-                    if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
-                        cx.emit_span_lint(
-                            OVERFLOWING_LITERALS,
-                            par_e.span,
-                            OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
-                        );
-                        return;
-                    }
-                }
-                _ => {}
-            }
-        }
-        if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) {
-            // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
-            return;
-        }
-        if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
-            report_bin_hex_error(
-                cx,
-                e,
-                attr::IntType::UnsignedInt(ty::ast_uint_ty(t)),
-                Integer::from_uint_ty(cx, t).size(),
-                repr_str,
-                lit_val,
-                false,
-            );
-            return;
-        }
-        cx.emit_span_lint(
-            OVERFLOWING_LITERALS,
-            e.span,
-            OverflowingUInt {
-                ty: t.name_str(),
-                lit: cx
-                    .sess()
-                    .source_map()
-                    .span_to_snippet(lit.span)
-                    .unwrap_or_else(|_| lit_val.to_string()),
-                min,
-                max,
-            },
-        );
-    }
-}
-
-fn lint_literal<'tcx>(
-    cx: &LateContext<'tcx>,
-    type_limits: &TypeLimits,
-    e: &'tcx hir::Expr<'tcx>,
-    lit: &hir::Lit,
-) {
-    match *cx.typeck_results().node_type(e.hir_id).kind() {
-        ty::Int(t) => {
-            match lit.node {
-                ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
-                    lint_int_literal(cx, type_limits, e, lit, t, v.get())
-                }
-                _ => bug!(),
-            };
-        }
-        ty::Uint(t) => lint_uint_literal(cx, e, lit, t),
-        ty::Float(t) => {
-            let (is_infinite, sym) = match lit.node {
-                ast::LitKind::Float(v, _) => match t {
-                    // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI
-                    // issues resolved).
-                    ty::FloatTy::F16 => (Ok(false), v),
-                    ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v),
-                    ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v),
-                    ty::FloatTy::F128 => (Ok(false), v),
-                },
-                _ => bug!(),
-            };
-            if is_infinite == Ok(true) {
-                cx.emit_span_lint(
-                    OVERFLOWING_LITERALS,
-                    e.span,
-                    OverflowingLiteral {
-                        ty: t.name_str(),
-                        lit: cx
-                            .sess()
-                            .source_map()
-                            .span_to_snippet(lit.span)
-                            .unwrap_or_else(|_| sym.to_string()),
-                    },
-                );
-            }
-        }
-        _ => {}
-    }
-}
-
 fn lint_nan<'tcx>(
     cx: &LateContext<'tcx>,
     e: &'tcx hir::Expr<'tcx>,
@@ -991,6 +595,8 @@ struct CTypesVisitorState<'tcx> {
     /// The original type being checked, before we recursed
     /// to any other types it contains.
     base_ty: Ty<'tcx>,
+    /// Number of times we recursed while checking the type
+    recursion_depth: usize,
 }
 
 enum FfiResult<'tcx> {
@@ -1296,12 +902,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         // Protect against infinite recursion, for example
         // `struct S(*mut S);`.
-        // FIXME: A recursion limit is necessary as well, for irregular
-        // recursive types.
         if !acc.cache.insert(ty) {
             return FfiSafe;
         }
 
+        // Additional recursion check for more complex types like
+        // `struct A<T> { v: *const A<A<T>>, ... }` for which the
+        // cache check above won't be enough (fixes #130310)
+        if !tcx.recursion_limit().value_within_limit(acc.recursion_depth) {
+            return FfiUnsafe {
+                ty: acc.base_ty,
+                reason: fluent::lint_improper_ctypes_recursion_limit_reached,
+                help: None,
+            };
+        }
+
+        acc.recursion_depth += 1;
+
         match *ty.kind() {
             ty::Adt(def, args) => {
                 if let Some(boxed) = ty.boxed_ty()
@@ -1644,7 +1261,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             return;
         }
 
-        let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
+        let mut acc =
+            CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty, recursion_depth: 0 };
         match self.check_type_for_ffi(&mut acc, ty) {
             FfiResult::FfiSafe => {}
             FfiResult::FfiPhantom(ty) => {
diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs
new file mode 100644
index 00000000000..67404be24b5
--- /dev/null
+++ b/compiler/rustc_lint/src/types/literal.rs
@@ -0,0 +1,386 @@
+use hir::{is_range_literal, ExprKind, Node};
+use rustc_middle::ty::layout::IntegerExt;
+use rustc_middle::ty::Ty;
+use rustc_middle::{bug, ty};
+use rustc_target::abi::{Integer, Size};
+use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+
+use crate::context::LintContext;
+use crate::lints::{
+    OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
+    OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
+    RangeEndpointOutOfRange, UseInclusiveRange,
+};
+use crate::types::{TypeLimits, OVERFLOWING_LITERALS};
+use crate::LateContext;
+
+/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`).
+/// Returns `true` iff the lint was emitted.
+fn lint_overflowing_range_endpoint<'tcx>(
+    cx: &LateContext<'tcx>,
+    lit: &hir::Lit,
+    lit_val: u128,
+    max: u128,
+    expr: &'tcx hir::Expr<'tcx>,
+    ty: &str,
+) -> bool {
+    // Look past casts to support cases like `0..256 as u8`
+    let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id)
+        && let ExprKind::Cast(_, _) = par_expr.kind
+    {
+        (par_expr, expr.span)
+    } else {
+        (expr, expr.span)
+    };
+
+    // We only want to handle exclusive (`..`) ranges,
+    // which are represented as `ExprKind::Struct`.
+    let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false };
+    let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false };
+    if !is_range_literal(struct_expr) {
+        return false;
+    };
+    let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false };
+
+    // We can suggest using an inclusive range
+    // (`..=`) instead only if it is the `end` that is
+    // overflowing and only by 1.
+    if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) {
+        return false;
+    };
+
+    use rustc_ast::{LitIntType, LitKind};
+    let suffix = match lit.node {
+        LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
+        LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
+        LitKind::Int(_, LitIntType::Unsuffixed) => "",
+        _ => bug!(),
+    };
+
+    let sub_sugg = if expr.span.lo() == lit_span.lo() {
+        let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false };
+        UseInclusiveRange::WithoutParen {
+            sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
+            start,
+            literal: lit_val - 1,
+            suffix,
+        }
+    } else {
+        UseInclusiveRange::WithParen {
+            eq_sugg: expr.span.shrink_to_lo(),
+            lit_sugg: lit_span,
+            literal: lit_val - 1,
+            suffix,
+        }
+    };
+
+    cx.emit_span_lint(
+        OVERFLOWING_LITERALS,
+        struct_expr.span,
+        RangeEndpointOutOfRange { ty, sub: sub_sugg },
+    );
+
+    // We've just emitted a lint, special cased for `(...)..MAX+1` ranges,
+    // return `true` so the callers don't also emit a lint
+    true
+}
+
+// For `isize` & `usize`, be conservative with the warnings, so that the
+// warnings are consistent between 32- and 64-bit platforms.
+pub(crate) fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) {
+    match int_ty {
+        ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()),
+        ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()),
+        ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()),
+        ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()),
+        ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()),
+        ty::IntTy::I128 => (i128::MIN, i128::MAX),
+    }
+}
+
+pub(crate) fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) {
+    let max = match uint_ty {
+        ty::UintTy::Usize => u64::MAX.into(),
+        ty::UintTy::U8 => u8::MAX.into(),
+        ty::UintTy::U16 => u16::MAX.into(),
+        ty::UintTy::U32 => u32::MAX.into(),
+        ty::UintTy::U64 => u64::MAX.into(),
+        ty::UintTy::U128 => u128::MAX,
+    };
+    (0, max)
+}
+
+fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> {
+    let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?;
+    let firstch = src.chars().next()?;
+
+    if firstch == '0' {
+        match src.chars().nth(1) {
+            Some('x' | 'b') => return Some(src),
+            _ => return None,
+        }
+    }
+
+    None
+}
+
+fn report_bin_hex_error(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    ty: attr::IntType,
+    size: Size,
+    repr_str: String,
+    val: u128,
+    negative: bool,
+) {
+    let (t, actually) = match ty {
+        attr::IntType::SignedInt(t) => {
+            let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) };
+            (t.name_str(), actually.to_string())
+        }
+        attr::IntType::UnsignedInt(t) => {
+            let actually = size.truncate(val);
+            (t.name_str(), actually.to_string())
+        }
+    };
+    let sign =
+        if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
+    let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
+        |suggestion_ty| {
+            if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+                let (sans_suffix, _) = repr_str.split_at(pos);
+                OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
+            } else {
+                OverflowingBinHexSub::Help { suggestion_ty }
+            }
+        },
+    );
+    let sign_bit_sub = (!negative)
+        .then(|| {
+            let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else {
+                return None;
+            };
+
+            let Some(bit_width) = int_ty.bit_width() else {
+                return None; // isize case
+            };
+
+            // Skip if sign bit is not set
+            if (val & (1 << (bit_width - 1))) == 0 {
+                return None;
+            }
+
+            let lit_no_suffix =
+                if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+                    repr_str.split_at(pos).0
+                } else {
+                    &repr_str
+                };
+
+            Some(OverflowingBinHexSignBitSub {
+                span: expr.span,
+                lit_no_suffix,
+                negative_val: actually.clone(),
+                int_ty: int_ty.name_str(),
+                uint_ty: int_ty.to_unsigned().name_str(),
+            })
+        })
+        .flatten();
+
+    cx.emit_span_lint(
+        OVERFLOWING_LITERALS,
+        expr.span,
+        OverflowingBinHex {
+            ty: t,
+            lit: repr_str.clone(),
+            dec: val,
+            actually,
+            sign,
+            sub,
+            sign_bit_sub,
+        },
+    )
+}
+
+// Find the "next" fitting integer and return a suggestion string
+//
+// No suggestion is offered for `{i,u}size`. Otherwise, we try to suggest an equal-sized type.
+fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> {
+    match t.kind() {
+        ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None,
+        ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()),
+        ty::Int(_) if negative => Some(Integer::fit_signed(-(val as i128)).int_ty_str()),
+        ty::Int(int) => {
+            let signed = Integer::fit_signed(val as i128);
+            let unsigned = Integer::fit_unsigned(val);
+            Some(if Some(unsigned.size().bits()) == int.bit_width() {
+                unsigned.uint_ty_str()
+            } else {
+                signed.int_ty_str()
+            })
+        }
+        _ => None,
+    }
+}
+
+fn lint_int_literal<'tcx>(
+    cx: &LateContext<'tcx>,
+    type_limits: &TypeLimits,
+    e: &'tcx hir::Expr<'tcx>,
+    lit: &hir::Lit,
+    t: ty::IntTy,
+    v: u128,
+) {
+    let int_type = t.normalize(cx.sess().target.pointer_width);
+    let (min, max) = int_ty_range(int_type);
+    let max = max as u128;
+    let negative = type_limits.negated_expr_id == Some(e.hir_id);
+
+    // Detect literal value out of range [min, max] inclusive
+    // avoiding use of -min to prevent overflow/panic
+    if (negative && v > max + 1) || (!negative && v > max) {
+        if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
+            report_bin_hex_error(
+                cx,
+                e,
+                attr::IntType::SignedInt(ty::ast_int_ty(t)),
+                Integer::from_int_ty(cx, t).size(),
+                repr_str,
+                v,
+                negative,
+            );
+            return;
+        }
+
+        if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) {
+            // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
+            return;
+        }
+
+        let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span };
+        let lit = cx
+            .sess()
+            .source_map()
+            .span_to_snippet(span)
+            .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() });
+        let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
+            .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
+
+        cx.emit_span_lint(
+            OVERFLOWING_LITERALS,
+            span,
+            OverflowingInt { ty: t.name_str(), lit, min, max, help },
+        );
+    }
+}
+
+fn lint_uint_literal<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx hir::Expr<'tcx>,
+    lit: &hir::Lit,
+    t: ty::UintTy,
+) {
+    let uint_type = t.normalize(cx.sess().target.pointer_width);
+    let (min, max) = uint_ty_range(uint_type);
+    let lit_val: u128 = match lit.node {
+        // _v is u8, within range by definition
+        ast::LitKind::Byte(_v) => return,
+        ast::LitKind::Int(v, _) => v.get(),
+        _ => bug!(),
+    };
+
+    if lit_val < min || lit_val > max {
+        if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) {
+            match par_e.kind {
+                hir::ExprKind::Cast(..) => {
+                    if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
+                        cx.emit_span_lint(
+                            OVERFLOWING_LITERALS,
+                            par_e.span,
+                            OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
+                        );
+                        return;
+                    }
+                }
+                _ => {}
+            }
+        }
+        if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) {
+            // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
+            return;
+        }
+        if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
+            report_bin_hex_error(
+                cx,
+                e,
+                attr::IntType::UnsignedInt(ty::ast_uint_ty(t)),
+                Integer::from_uint_ty(cx, t).size(),
+                repr_str,
+                lit_val,
+                false,
+            );
+            return;
+        }
+        cx.emit_span_lint(
+            OVERFLOWING_LITERALS,
+            e.span,
+            OverflowingUInt {
+                ty: t.name_str(),
+                lit: cx
+                    .sess()
+                    .source_map()
+                    .span_to_snippet(lit.span)
+                    .unwrap_or_else(|_| lit_val.to_string()),
+                min,
+                max,
+            },
+        );
+    }
+}
+
+pub(crate) fn lint_literal<'tcx>(
+    cx: &LateContext<'tcx>,
+    type_limits: &TypeLimits,
+    e: &'tcx hir::Expr<'tcx>,
+    lit: &hir::Lit,
+) {
+    match *cx.typeck_results().node_type(e.hir_id).kind() {
+        ty::Int(t) => {
+            match lit.node {
+                ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
+                    lint_int_literal(cx, type_limits, e, lit, t, v.get())
+                }
+                _ => bug!(),
+            };
+        }
+        ty::Uint(t) => lint_uint_literal(cx, e, lit, t),
+        ty::Float(t) => {
+            let (is_infinite, sym) = match lit.node {
+                ast::LitKind::Float(v, _) => match t {
+                    // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI
+                    // issues resolved).
+                    ty::FloatTy::F16 => (Ok(false), v),
+                    ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v),
+                    ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v),
+                    ty::FloatTy::F128 => (Ok(false), v),
+                },
+                _ => bug!(),
+            };
+            if is_infinite == Ok(true) {
+                cx.emit_span_lint(
+                    OVERFLOWING_LITERALS,
+                    e.span,
+                    OverflowingLiteral {
+                        ty: t.name_str(),
+                        lit: cx
+                            .sess()
+                            .source_map()
+                            .span_to_snippet(lit.span)
+                            .unwrap_or_else(|_| sym.to_string()),
+                    },
+                );
+            }
+        }
+        _ => {}
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 6708ae60562..1a584cf2890 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -521,6 +521,10 @@ impl<'tcx> Const<'tcx> {
         self.try_to_valtree()?.try_to_scalar()
     }
 
+    pub fn try_to_bool(self) -> Option<bool> {
+        self.try_to_scalar()?.to_bool().ok()
+    }
+
     #[inline]
     pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
         self.try_to_valtree()?.try_to_target_usize(tcx)
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index a0a0dd058ff..9c2f869f357 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -145,7 +145,7 @@ fn encode_const<'tcx>(
                     let _ = write!(s, "{val}");
                 }
                 ty::Bool => {
-                    let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
+                    let val = c.try_to_bool().expect("expected monomorphic const in cfi");
                     let _ = write!(s, "{val}");
                 }
                 _ => {
@@ -411,7 +411,7 @@ pub fn encode_ty<'tcx>(
 
         ty::Array(ty0, len) => {
             // A<array-length><element-type>
-            let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
+            let len = len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi");
             let mut s = String::from("A");
             let _ = write!(s, "{len}");
             s.push_str(&encode_ty(tcx, *ty0, dict, options));
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index 34763adde4f..e4231d75506 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -146,7 +146,10 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
                         !is_zst
                     });
                     if let Some(field) = field {
-                        let ty0 = self.tcx.erase_regions(field.ty(self.tcx, args));
+                        let ty0 = self.tcx.normalize_erasing_regions(
+                            ty::ParamEnv::reveal_all(),
+                            field.ty(self.tcx, args),
+                        );
                         // Generalize any repr(transparent) user-defined type that is either a
                         // pointer or reference, and either references itself or any other type that
                         // contains or references itself, to avoid a reference cycle.
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 99689511854..55f3b628ce8 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -1991,6 +1991,11 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
 /// * The file doesn't exist.
 /// * The user lacks permissions to remove the file.
 ///
+/// This function will only ever return an error of kind `NotFound` if the given
+/// path does not exist. Note that the inverse is not true,
+/// ie. if a path does not exist, its removal may fail for a number of reasons,
+/// such as insufficient permissions.
+///
 /// # Examples
 ///
 /// ```no_run
@@ -2448,6 +2453,11 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// * The user lacks permissions to remove the directory at the provided `path`.
 /// * The directory isn't empty.
 ///
+/// This function will only ever return an error of kind `NotFound` if the given
+/// path does not exist. Note that the inverse is not true,
+/// ie. if a path does not exist, its removal may fail for a number of reasons,
+/// such as insufficient permissions.
+///
 /// # Examples
 ///
 /// ```no_run
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index a7e9352bb1c..2047345d78a 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1733,6 +1733,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
 
         let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js");
 
+        if mode == "run-make" {
+            let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host });
+            cmd.arg("--cargo-path").arg(cargo);
+        }
+
         // Avoid depending on rustdoc when we don't need it.
         if mode == "rustdoc"
             || mode == "run-make"
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 5c18179b6fe..414f9f3a7f1 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -183,6 +183,9 @@ pub struct Config {
     /// The rustc executable.
     pub rustc_path: PathBuf,
 
+    /// The cargo executable.
+    pub cargo_path: Option<PathBuf>,
+
     /// The rustdoc executable.
     pub rustdoc_path: Option<PathBuf>,
 
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 250b5084d13..3339116d542 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -47,6 +47,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
     opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
         .reqopt("", "run-lib-path", "path to target shared libraries", "PATH")
         .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH")
+        .optopt("", "cargo-path", "path to cargo to use for compiling", "PATH")
         .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH")
         .optopt("", "coverage-dump-path", "path to coverage-dump to use in tests", "PATH")
         .reqopt("", "python", "path to python to use for doc tests", "PATH")
@@ -260,6 +261,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
         run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
         rustc_path: opt_path(matches, "rustc-path"),
+        cargo_path: matches.opt_str("cargo-path").map(PathBuf::from),
         rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
         coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from),
         python: matches.opt_str("python").unwrap(),
@@ -364,6 +366,7 @@ pub fn log_config(config: &Config) {
     logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
     logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
     logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
+    logv(c, format!("cargo_path: {:?}", config.cargo_path));
     logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
     logv(c, format!("src_base: {:?}", config.src_base.display()));
     logv(c, format!("build_base: {:?}", config.build_base.display()));
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 852568ae925..75fe6a6baaf 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -61,6 +61,10 @@ impl TestCx<'_> {
             .env_remove("MFLAGS")
             .env_remove("CARGO_MAKEFLAGS");
 
+        if let Some(ref cargo) = self.config.cargo_path {
+            cmd.env("CARGO", cwd.join(cargo));
+        }
+
         if let Some(ref rustdoc) = self.config.rustdoc_path {
             cmd.env("RUSTDOC", cwd.join(rustdoc));
         }
@@ -409,6 +413,10 @@ impl TestCx<'_> {
             // through a specific CI runner).
             .env("LLVM_COMPONENTS", &self.config.llvm_components);
 
+        if let Some(ref cargo) = self.config.cargo_path {
+            cmd.env("CARGO", source_root.join(cargo));
+        }
+
         if let Some(ref rustdoc) = self.config.rustdoc_path {
             cmd.env("RUSTDOC", source_root.join(rustdoc));
         }
diff --git a/tests/crashes/130310.rs b/tests/crashes/130310.rs
deleted file mode 100644
index d59dd39983c..00000000000
--- a/tests/crashes/130310.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ known-bug: rust-lang/rust#130310
-
-use std::marker::PhantomData;
-
-#[repr(C)]
-struct A<T> {
-    a: *const A<A<T>>,
-    p: PhantomData<T>,
-}
-
-extern "C" {
-    fn f(a: *const A<()>);
-}
-
-fn main() {}
diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs
index 42ed07d9daf..3b05fe2055c 100644
--- a/tests/run-make/compiler-builtins/rmake.rs
+++ b/tests/run-make/compiler-builtins/rmake.rs
@@ -33,8 +33,8 @@ fn main() {
 
     let path = env_var("PATH");
     let rustc = env_var("RUSTC");
-    let bootstrap_cargo = env_var("BOOTSTRAP_CARGO");
-    let mut cmd = cmd(bootstrap_cargo);
+    let cargo = env_var("CARGO");
+    let mut cmd = cmd(cargo);
     cmd.args(&[
         "build",
         "--manifest-path",
diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make/thumb-none-cortex-m/rmake.rs
index 0ddb91d378f..9112646290f 100644
--- a/tests/run-make/thumb-none-cortex-m/rmake.rs
+++ b/tests/run-make/thumb-none-cortex-m/rmake.rs
@@ -36,10 +36,10 @@ fn main() {
 
     let path = env_var("PATH");
     let rustc = env_var("RUSTC");
-    let bootstrap_cargo = env_var("BOOTSTRAP_CARGO");
-    // FIXME: extract bootstrap cargo invocations to a proper command
+    let cargo = env_var("CARGO");
+    // FIXME: extract cargo invocations to a proper command
     // https://github.com/rust-lang/rust/issues/128734
-    let mut cmd = cmd(bootstrap_cargo);
+    let mut cmd = cmd(cargo);
     cmd.args(&[
         "build",
         "--manifest-path",
diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs
index c6dd981cced..5303b247173 100644
--- a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs
+++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs
@@ -3,8 +3,7 @@
 
 impl EntriesBuffer {
     fn a(&self) -> impl Iterator {
-        self.0.iter_mut() //~ ERROR: cannot borrow `*self.0` as mutable, as it is behind a `&` reference
-                          //~| ERROR captures lifetime that does not appear in bounds
+        self.0.iter_mut()
     }
 }
 
diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr
index 24f3ed7cdf1..fcbd6904599 100644
--- a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr
@@ -1,5 +1,5 @@
 error[E0425]: cannot find value `HashesEntryLEN` in this scope
-  --> $DIR/issue-109141.rs:11:32
+  --> $DIR/issue-109141.rs:10:32
    |
 LL | struct EntriesBuffer(Box<[[u8; HashesEntryLEN]; 5]>);
    |                                ^^^^^^^^^^^^^^ not found in this scope
@@ -9,33 +9,6 @@ help: you might be missing a const parameter
 LL | struct EntriesBuffer<const HashesEntryLEN: /* Type */>(Box<[[u8; HashesEntryLEN]; 5]>);
    |                     ++++++++++++++++++++++++++++++++++
 
-error[E0596]: cannot borrow `*self.0` as mutable, as it is behind a `&` reference
-  --> $DIR/issue-109141.rs:6:9
-   |
-LL |         self.0.iter_mut()
-   |         ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
-   |
-help: consider changing this to be a mutable reference
-   |
-LL |     fn a(&mut self) -> impl Iterator {
-   |          ~~~~~~~~~
-
-error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds
-  --> $DIR/issue-109141.rs:6:9
-   |
-LL |     fn a(&self) -> impl Iterator {
-   |          -----     ------------- opaque type defined here
-   |          |
-   |          hidden type `std::slice::IterMut<'_, [u8; {const error}]>` captures the anonymous lifetime defined here
-LL |         self.0.iter_mut()
-   |         ^^^^^^^^^^^^^^^^^
-   |
-help: add a `use<...>` bound to explicitly capture `'_`
-   |
-LL |     fn a(&self) -> impl Iterator + use<'_> {
-   |                                  +++++++++
-
-error: aborting due to 3 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0425, E0596, E0700.
-For more information about an error, try `rustc --explain E0425`.
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs
index 7209290a36e..56b8acbf88c 100644
--- a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs
+++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs
@@ -2,7 +2,6 @@
 #![allow(incomplete_features)]
 
 type Foo = impl Sized;
-//~^ ERROR: unconstrained opaque type
 
 fn with_bound<const N: usize>() -> Foo
 where
diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr
index c7a266205b4..e9fb8c0f403 100644
--- a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/opaque_type.rs:11:17
+  --> $DIR/opaque_type.rs:10:17
    |
 LL | type Foo = impl Sized;
    |            ---------- the found opaque type
@@ -11,20 +11,12 @@ LL |     let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize];
            found opaque type `Foo`
 
 error[E0605]: non-primitive cast: `usize` as `Foo`
-  --> $DIR/opaque_type.rs:11:17
+  --> $DIR/opaque_type.rs:10:17
    |
 LL |     let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize];
    |                 ^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
 
-error: unconstrained opaque type
-  --> $DIR/opaque_type.rs:4:12
-   |
-LL | type Foo = impl Sized;
-   |            ^^^^^^^^^^
-   |
-   = note: `Foo` must be used in combination with a concrete type within the same module
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0308, E0605.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/consts/issue-116186.rs b/tests/ui/consts/issue-116186.rs
index a77c38c64dc..8bfb47629e7 100644
--- a/tests/ui/consts/issue-116186.rs
+++ b/tests/ui/consts/issue-116186.rs
@@ -4,7 +4,7 @@
 fn something(path: [usize; N]) -> impl Clone {
     //~^ ERROR cannot find value `N` in this scope
     match path {
-        [] => 0, //~ ERROR cannot pattern-match on an array without a fixed length
+        [] => 0,
         _ => 1,
     };
 }
diff --git a/tests/ui/consts/issue-116186.stderr b/tests/ui/consts/issue-116186.stderr
index e6eae2d9f55..46931f79dd0 100644
--- a/tests/ui/consts/issue-116186.stderr
+++ b/tests/ui/consts/issue-116186.stderr
@@ -9,13 +9,6 @@ help: you might be missing a const parameter
 LL | fn something<const N: /* Type */>(path: [usize; N]) -> impl Clone {
    |             +++++++++++++++++++++
 
-error[E0730]: cannot pattern-match on an array without a fixed length
-  --> $DIR/issue-116186.rs:7:9
-   |
-LL |         [] => 0,
-   |         ^^
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0425, E0730.
-For more information about an error, try `rustc --explain E0425`.
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.rs b/tests/ui/lint/improper-types-stack-overflow-130310.rs
new file mode 100644
index 00000000000..60eb8739817
--- /dev/null
+++ b/tests/ui/lint/improper-types-stack-overflow-130310.rs
@@ -0,0 +1,20 @@
+// Regression test for #130310
+// Tests that we do not fall into infinite
+// recursion while checking FFI safety of
+// recursive types like `A<T>` below
+
+//@ build-pass
+use std::marker::PhantomData;
+
+#[repr(C)]
+struct A<T> {
+    a: *const A<A<T>>, // Recursive because of this field
+    p: PhantomData<T>,
+}
+
+extern "C" {
+    fn f(a: *const A<()>);
+    //~^ WARN `extern` block uses type `*const A<()>`, which is not FFI-safe
+}
+
+fn main() {}
diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.stderr b/tests/ui/lint/improper-types-stack-overflow-130310.stderr
new file mode 100644
index 00000000000..6981bb25755
--- /dev/null
+++ b/tests/ui/lint/improper-types-stack-overflow-130310.stderr
@@ -0,0 +1,11 @@
+warning: `extern` block uses type `*const A<()>`, which is not FFI-safe
+  --> $DIR/improper-types-stack-overflow-130310.rs:16:13
+   |
+LL |     fn f(a: *const A<()>);
+   |             ^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: type is infinitely recursive
+   = note: `#[warn(improper_ctypes)]` on by default
+
+warning: 1 warning emitted
+