From 43a79a0f4bf842c5e6a4ada2e6280fc9535c18ee Mon Sep 17 00:00:00 2001
From: Scott McMurray <scottmcm@users.noreply.github.com>
Date: Sun, 15 Dec 2024 22:18:09 -0800
Subject: [PATCH] Check for array lengths that aren't actually `usize`

---
 .../src/build/expr/as_place.rs                | 32 +++++++++++++++----
 .../issues/index_array_bad_type.rs            | 15 +++++++++
 .../issues/index_array_bad_type.stderr        |  8 +++++
 3 files changed, 48 insertions(+), 7 deletions(-)
 create mode 100644 tests/ui/const-generics/issues/index_array_bad_type.rs
 create mode 100644 tests/ui/const-generics/issues/index_array_bad_type.stderr

diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
index 70a74910a68c6..89a1f06d3d16f 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -647,13 +647,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         match place_ty.kind() {
             ty::Array(_elem_ty, len_const) => {
-                // We know how long an array is, so just use that as a constant
-                // directly -- no locals needed. We do need one statement so
-                // that borrow- and initialization-checking consider it used,
-                // though. FIXME: Do we really *need* to count this as a use?
-                // Could partial array tracking work off something else instead?
-                self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place);
-                let const_ = Const::from_ty_const(*len_const, usize_ty, self.tcx);
+                let ty_const = if let Some((_, len_ty)) = len_const.try_to_valtree()
+                    && len_ty != self.tcx.types.usize
+                {
+                    // Bad const generics can give us a constant from the type that's
+                    // not actually a `usize`, so in that case give an error instead.
+                    // FIXME: It'd be nice if the type checker made sure this wasn't
+                    // possible, instead.
+                    let err = self.tcx.dcx().span_delayed_bug(
+                        span,
+                        format!(
+                            "Array length should have already been a type error, as it's {len_ty:?}"
+                        ),
+                    );
+                    ty::Const::new_error(self.tcx, err)
+                } else {
+                    // We know how long an array is, so just use that as a constant
+                    // directly -- no locals needed. We do need one statement so
+                    // that borrow- and initialization-checking consider it used,
+                    // though. FIXME: Do we really *need* to count this as a use?
+                    // Could partial array tracking work off something else instead?
+                    self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place);
+                    *len_const
+                };
+
+                let const_ = Const::from_ty_const(ty_const, usize_ty, self.tcx);
                 Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ }))
             }
             ty::Slice(_elem_ty) => {
diff --git a/tests/ui/const-generics/issues/index_array_bad_type.rs b/tests/ui/const-generics/issues/index_array_bad_type.rs
new file mode 100644
index 0000000000000..41e4dba026c2e
--- /dev/null
+++ b/tests/ui/const-generics/issues/index_array_bad_type.rs
@@ -0,0 +1,15 @@
+//@ check-fail
+//@ compile-flags: -C opt-level=0
+
+#![crate_type = "lib"]
+
+// This used to fail in the known-panics lint, as the MIR was ill-typed due to
+// the length constant not actually having type usize.
+// https://github.com/rust-lang/rust/issues/134352
+
+pub struct BadStruct<const N: i64>(pub [u8; N]);
+//~^ ERROR: the constant `N` is not of type `usize`
+
+pub fn bad_array_length_type(value: BadStruct<3>) -> u8 {
+    value.0[0]
+}
diff --git a/tests/ui/const-generics/issues/index_array_bad_type.stderr b/tests/ui/const-generics/issues/index_array_bad_type.stderr
new file mode 100644
index 0000000000000..e4417192150ee
--- /dev/null
+++ b/tests/ui/const-generics/issues/index_array_bad_type.stderr
@@ -0,0 +1,8 @@
+error: the constant `N` is not of type `usize`
+  --> $DIR/index_array_bad_type.rs:10:40
+   |
+LL | pub struct BadStruct<const N: i64>(pub [u8; N]);
+   |                                        ^^^^^^^ expected `usize`, found `i64`
+
+error: aborting due to 1 previous error
+