From 9a2856837c6a09b39b4830c0cfeecf2f68b27e8e Mon Sep 17 00:00:00 2001
From: Boxy <rust@boxyuwu.dev>
Date: Thu, 10 Apr 2025 15:11:55 +0100
Subject: [PATCH] GAI logic on stable too

---
 compiler/rustc_hir_typeck/src/expr.rs         | 51 +--------------
 .../rustc_hir_typeck/src/fn_ctxt/checks.rs    | 48 +++++++++++++-
 .../lang-item-generic-requirements.rs         |  2 +
 .../lang-item-generic-requirements.stderr     | 20 +++++-
 ...py-check-const-element-uninferred-count.rs | 65 +++++++++++++++++++
 ...heck-const-element-uninferred-count.stderr | 36 ++++++++++
 .../copy-inference-side-effects-are-lazy.rs   |  9 +--
 ...opy-inference-side-effects-are-lazy.stderr | 17 +++++
 8 files changed, 186 insertions(+), 62 deletions(-)
 create mode 100644 tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs
 create mode 100644 tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr
 create mode 100644 tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr

diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 3475d15e948..56167c8b7b3 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1868,62 +1868,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // We defer checking whether the element type is `Copy` as it is possible to have
         // an inference variable as a repeat count and it seems unlikely that `Copy` would
         // have inference side effects required for type checking to succeed.
-        if tcx.features().generic_arg_infer() {
-            self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
-        // If the length is 0, we don't create any elements, so we don't copy any.
-        // If the length is 1, we don't copy that one element, we move it. Only check
-        // for `Copy` if the length is larger, or unevaluated.
-        } else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
-            self.enforce_repeat_element_needs_copy_bound(element, element_ty);
-        }
+        self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
 
         let ty = Ty::new_array_with_const_len(tcx, t, count);
         self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None));
         ty
     }
 
-    /// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
-    pub(super) fn enforce_repeat_element_needs_copy_bound(
-        &self,
-        element: &hir::Expr<'_>,
-        element_ty: Ty<'tcx>,
-    ) {
-        let tcx = self.tcx;
-        // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
-        match &element.kind {
-            hir::ExprKind::ConstBlock(..) => return,
-            hir::ExprKind::Path(qpath) => {
-                let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
-                if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
-                {
-                    return;
-                }
-            }
-            _ => {}
-        }
-        // If someone calls a const fn or constructs a const value, they can extract that
-        // out into a separate constant (or a const block in the future), so we check that
-        // to tell them that in the diagnostic. Does not affect typeck.
-        let is_constable = match element.kind {
-            hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
-                ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn,
-                _ => traits::IsConstable::No,
-            },
-            hir::ExprKind::Path(qpath) => {
-                match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
-                    Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
-                    _ => traits::IsConstable::No,
-                }
-            }
-            _ => traits::IsConstable::No,
-        };
-
-        let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
-        let code =
-            traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span };
-        self.require_type_meets(element_ty, element.span, code, lang_item);
-    }
-
     fn check_expr_tuple(
         &self,
         elts: &'tcx [hir::Expr<'tcx>],
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index cdced64b53c..403d0bede33 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -4,10 +4,10 @@ use itertools::Itertools;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize};
-use rustc_hir::def::{CtorOf, DefKind, Res};
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::{ExprKind, HirId, Node, QPath};
+use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath};
 use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
 use rustc_hir_analysis::check::potentially_plural_count;
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
@@ -162,6 +162,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
+    pub(super) fn enforce_repeat_element_needs_copy_bound(
+        &self,
+        element: &hir::Expr<'_>,
+        element_ty: Ty<'tcx>,
+    ) {
+        // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
+        match &element.kind {
+            hir::ExprKind::ConstBlock(..) => return,
+            hir::ExprKind::Path(qpath) => {
+                let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
+                if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
+                {
+                    return;
+                }
+            }
+            _ => {}
+        }
+
+        // If someone calls a const fn or constructs a const value, they can extract that
+        // out into a separate constant (or a const block in the future), so we check that
+        // to tell them that in the diagnostic. Does not affect typeck.
+        let is_constable = match element.kind {
+            hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
+                ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => {
+                    traits::IsConstable::Fn
+                }
+                _ => traits::IsConstable::No,
+            },
+            hir::ExprKind::Path(qpath) => {
+                match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
+                    Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
+                    _ => traits::IsConstable::No,
+                }
+            }
+            _ => traits::IsConstable::No,
+        };
+
+        let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
+        let code =
+            traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span };
+        self.require_type_meets(element_ty, element.span, code, lang_item);
+    }
+
     pub(in super::super) fn check_method_argument_types(
         &self,
         sp: Span,
diff --git a/tests/ui/lang-items/lang-item-generic-requirements.rs b/tests/ui/lang-items/lang-item-generic-requirements.rs
index 90ed5f3f0ef..7676b5557d2 100644
--- a/tests/ui/lang-items/lang-item-generic-requirements.rs
+++ b/tests/ui/lang-items/lang-item-generic-requirements.rs
@@ -49,12 +49,14 @@ fn ice() {
     // Use index
     let arr = [0; 5];
     let _ = arr[2];
+    //~^ ERROR cannot index into a value of type `[{integer}; 5]`
 
     // Use phantomdata
     let _ = MyPhantomData::<(), i32>;
 
     // Use Foo
     let _: () = Foo;
+    //~^ ERROR mismatched types
 }
 
 // use `start`
diff --git a/tests/ui/lang-items/lang-item-generic-requirements.stderr b/tests/ui/lang-items/lang-item-generic-requirements.stderr
index 3de67d65940..409fa05d637 100644
--- a/tests/ui/lang-items/lang-item-generic-requirements.stderr
+++ b/tests/ui/lang-items/lang-item-generic-requirements.stderr
@@ -76,9 +76,23 @@ LL |     r + a;
    |     |
    |     {integer}
 
+error[E0608]: cannot index into a value of type `[{integer}; 5]`
+  --> $DIR/lang-item-generic-requirements.rs:51:16
+   |
+LL |     let _ = arr[2];
+   |                ^^^
+
+error[E0308]: mismatched types
+  --> $DIR/lang-item-generic-requirements.rs:58:17
+   |
+LL |     let _: () = Foo;
+   |            --   ^^^ expected `()`, found `Foo`
+   |            |
+   |            expected due to this
+
 error: requires `copy` lang_item
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
-Some errors have detailed explanations: E0369, E0392, E0718.
-For more information about an error, try `rustc --explain E0369`.
+Some errors have detailed explanations: E0308, E0369, E0392, E0608, E0718.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs
new file mode 100644
index 00000000000..8ef0c2690ba
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs
@@ -0,0 +1,65 @@
+#![feature(generic_arg_infer)]
+
+// Test when deferring repeat expr copy checks to end of typechecking whether elements
+// that are const items allow for repeat counts to go uninferred without an error being
+// emitted if they would later wind up inferred by integer fallback.
+//
+// This test should be updated if we wind up deferring repeat expr checks until *after*
+// integer fallback as the point of the test is not *specifically* about integer fallback
+// but rather about the behaviour of `const` element exprs.
+
+trait Trait<const N: usize> {}
+
+// We impl `Trait` for both `i32` and `u32` to avoid being able
+// to prove `?int: Trait<?n>` from there only being one impl.
+impl Trait<2> for i32 {}
+impl Trait<2> for u32 {}
+
+fn tie_and_make_goal<const N: usize, T: Trait<N>>(_: &T, _: &[String; N]) {}
+
+fn const_block() {
+    // Deferred repeat expr `String; ?n`
+    let a = [const { String::new() }; _];
+    //~^ ERROR: type annotations needed for `[String; _]`
+
+    // `?int: Trait<?n>` goal
+    tie_and_make_goal(&1, &a);
+
+    // If repeat expr checks structurally resolve the `?n`s before checking if the
+    // element is a `const` then we would error here. Otherwise we avoid doing so,
+    // integer fallback occurs, allowing `?int: Trait<?n>` goals to make progress,
+    // inferring the repeat counts (to `2` but that doesn't matter as the element is `const`).
+}
+
+fn const_item() {
+    const MY_CONST: String = String::new();
+
+    // Deferred repeat expr `String; ?n`
+    let a = [MY_CONST; _];
+    //~^ ERROR: type annotations needed for `[String; _]`
+
+    // `?int: Trait<?n>` goal
+    tie_and_make_goal(&1, &a);
+
+    // ... same as `const_block`
+}
+
+fn assoc_const() {
+    trait Dummy {
+        const ASSOC: String;
+    }
+    impl Dummy for () {
+        const ASSOC: String = String::new();
+    }
+
+    // Deferred repeat expr `String; ?n`
+    let a = [<() as Dummy>::ASSOC; _];
+    //~^ ERROR: type annotations needed for `[String; _]`
+
+    // `?int: Trait<?n>` goal
+    tie_and_make_goal(&1, &a);
+
+    // ... same as `const_block`
+}
+
+fn main() {}
diff --git a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr
new file mode 100644
index 00000000000..8229b0b2b37
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr
@@ -0,0 +1,36 @@
+error[E0282]: type annotations needed for `[String; _]`
+  --> $DIR/copy-check-const-element-uninferred-count.rs:22:9
+   |
+LL |     let a = [const { String::new() }; _];
+   |         ^    ----------------------- type must be known at this point
+   |
+help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
+   |
+LL |     let a: [_; N] = [const { String::new() }; _];
+   |          ++++++++
+
+error[E0282]: type annotations needed for `[String; _]`
+  --> $DIR/copy-check-const-element-uninferred-count.rs:38:9
+   |
+LL |     let a = [MY_CONST; _];
+   |         ^    -------- type must be known at this point
+   |
+help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
+   |
+LL |     let a: [_; N] = [MY_CONST; _];
+   |          ++++++++
+
+error[E0282]: type annotations needed for `[String; _]`
+  --> $DIR/copy-check-const-element-uninferred-count.rs:56:9
+   |
+LL |     let a = [<() as Dummy>::ASSOC; _];
+   |         ^    -------------------- type must be known at this point
+   |
+help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
+   |
+LL |     let a: [_; N] = [<() as Dummy>::ASSOC; _];
+   |          ++++++++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs
index 0b0672d9c2b..d50466ac4bb 100644
--- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs
+++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs
@@ -1,8 +1,3 @@
-//@revisions: current gai
-//@[current] check-pass
-
-#![cfg_attr(gai, feature(generic_arg_infer))]
-
 use std::marker::PhantomData;
 
 struct Foo<T>(PhantomData<T>);
@@ -20,6 +15,6 @@ fn extract<T, const N: usize>(_: [Foo<T>; N]) -> T {
 
 fn main() {
     let x = [Foo(PhantomData); 2];
-    //[gai]~^ ERROR: type annotations needed
-    _ = extract(x).max(2);
+    //~^ ERROR: type annotations needed
+    extract(x).max(2);
 }
diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr
new file mode 100644
index 00000000000..ba44beb76db
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr
@@ -0,0 +1,17 @@
+error[E0282]: type annotations needed for `[Foo<_>; 2]`
+  --> $DIR/copy-inference-side-effects-are-lazy.rs:17:9
+   |
+LL |     let x = [Foo(PhantomData); 2];
+   |         ^
+LL |
+LL |     extract(x).max(2);
+   |     ---------- type must be known at this point
+   |
+help: consider giving `x` an explicit type, where the type for type parameter `T` is specified
+   |
+LL |     let x: [Foo<T>; 2] = [Foo(PhantomData); 2];
+   |          +++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0282`.