about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBoxy <rust@boxyuwu.dev>2025-04-10 15:30:28 +0100
committerBoxy <rust@boxyuwu.dev>2025-05-21 20:38:49 +0100
commit508a9f085393a1e348725ca7f169780c65dbc212 (patch)
treef7d2692901713862c0c724888ea7103372fc4457
parent77a2fc60a11838d93df08a7a31ba47bcc828f750 (diff)
downloadrust-508a9f085393a1e348725ca7f169780c65dbc212.tar.gz
rust-508a9f085393a1e348725ca7f169780c65dbc212.zip
Check for element being `const` before resolving repeat count
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs95
-rw-r--r--tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs13
-rw-r--r--tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr37
3 files changed, 65 insertions, 80 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 7a0b4c22a19..dc6a9dc93d0 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -108,6 +108,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let deferred_repeat_expr_checks = deferred_repeat_expr_checks
             .drain(..)
             .flat_map(|(element, element_ty, count)| {
+                // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy
+                // so we don't need to attempt to structurally resolve the repeat count which may unnecessarily error.
+                match &element.kind {
+                    hir::ExprKind::ConstBlock(..) => return None,
+                    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 None;
+                        }
+                    }
+                    _ => {}
+                }
+
                 // We want to emit an error if the const is not structurally resolveable as otherwise
                 // we can find up conservatively proving `Copy` which may infer the repeat expr count
                 // to something that never required `Copy` in the first place.
@@ -128,12 +145,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // expr's `Copy` check.
             .collect::<Vec<_>>();
 
+        let enforce_copy_bound = |element: &hir::Expr<'_>, element_ty| {
+            // 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);
+        };
+
         for (element, element_ty, count) in deferred_repeat_expr_checks {
             match count.kind() {
                 ty::ConstKind::Value(val)
                     if val.try_to_target_usize(self.tcx).is_none_or(|count| count > 1) =>
                 {
-                    self.enforce_repeat_element_needs_copy_bound(element, element_ty)
+                    enforce_copy_bound(element, element_ty)
                 }
                 // If the length is 0 or 1 we don't actually copy the element, we either don't create it
                 // or we just use the one value.
@@ -144,9 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ty::ConstKind::Param(_)
                 | ty::ConstKind::Expr(_)
                 | ty::ConstKind::Placeholder(_)
-                | ty::ConstKind::Unevaluated(_) => {
-                    self.enforce_repeat_element_needs_copy_bound(element, element_ty)
-                }
+                | ty::ConstKind::Unevaluated(_) => enforce_copy_bound(element, element_ty),
 
                 ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => {
                     unreachable!()
@@ -155,50 +198,6 @@ 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);
-    }
-
     /// Generic function that factors out common logic from function calls,
     /// method calls and overloaded operators.
     pub(in super::super) fn check_argument_types(
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
index 8ef0c2690ba..6115146539c 100644
--- a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs
+++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs
@@ -20,7 +20,6 @@ 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);
@@ -36,7 +35,6 @@ fn const_item() {
 
     // 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);
@@ -54,7 +52,6 @@ fn assoc_const() {
 
     // 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);
@@ -62,4 +59,14 @@ fn assoc_const() {
     // ... same as `const_block`
 }
 
+fn const_block_but_uninferred() {
+    // Deferred repeat expr `String; ?n`
+    let a = [const { String::new() }; _];
+    //~^ ERROR: type annotations needed for `[String; _]`
+
+    // Even if we don't structurally resolve the repeat count as part of repeat expr
+    // checks, we still error on the repeat count being uninferred as we require all
+    // types/consts to be inferred by the end of type checking.
+}
+
 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
index 8229b0b2b37..2f52537fa94 100644
--- a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr
+++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr
@@ -1,36 +1,15 @@
-error[E0282]: type annotations needed for `[String; _]`
-  --> $DIR/copy-check-const-element-uninferred-count.rs:22:9
+error[E0284]: type annotations needed for `[String; _]`
+  --> $DIR/copy-check-const-element-uninferred-count.rs:64:9
    |
 LL |     let a = [const { String::new() }; _];
-   |         ^    ----------------------- type must be known at this point
+   |         ^   ---------------------------- type must be known at this point
    |
-help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
+   = note: the length of array `[String; _]` must be type `usize`
+help: consider giving `a` an explicit type, where the placeholders `_` are specified
    |
-LL |     let a: [_; N] = [const { String::new() }; _];
+LL |     let a: [_; _] = [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
+error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0282`.
+For more information about this error, try `rustc --explain E0284`.