about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBoxy <rust@boxyuwu.dev>2025-04-08 17:01:29 +0100
committerBoxy <rust@boxyuwu.dev>2025-05-21 20:38:49 +0100
commitf60bab475e47e84bb188e463b6a17894a57092c4 (patch)
tree18273bf8977cc8a04f3584db9e4f08299c1ed350
parent356f2d077498d0ebe5ec4f9cfef04293a2b17611 (diff)
downloadrust-f60bab475e47e84bb188e463b6a17894a57092c4.tar.gz
rust-f60bab475e47e84bb188e463b6a17894a57092c4.zip
Don't allow repeat expr count inference side effects to propagate
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs63
-rw-r--r--tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs3
-rw-r--r--tests/ui/repeat-expr/copy-check-deferred-before-fallback.stderr14
-rw-r--r--tests/ui/repeat-expr/copy-check-inference-side-effects.rs34
-rw-r--r--tests/ui/repeat-expr/copy-check-inference-side-effects.stderr28
5 files changed, 123 insertions, 19 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index d2cdfe22a3a..533b5b2be79 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -104,24 +104,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(in super::super) fn check_repeat_exprs(&self) {
         let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
         debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
-        for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
-            // 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.
-            let count =
-                self.structurally_resolve_const(element.span, self.normalize(element.span, count));
-
-            // Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
-            // is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
-            if count.references_error() {
-                continue;
-            }
 
-            // 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.
-            if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
-                self.enforce_repeat_element_needs_copy_bound(element, element_ty);
+        let deferred_repeat_expr_checks = deferred_repeat_expr_checks
+            .drain(..)
+            .flat_map(|(element, element_ty, count)| {
+                // 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.
+                let count = self
+                    .structurally_resolve_const(element.span, self.normalize(element.span, count));
+
+                // Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
+                // is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
+                if count.references_error() {
+                    return None;
+                }
+
+                Some((element, element_ty, count))
+            })
+            // We collect to force the side effects of structurally resolving the repeat count to happen in one
+            // go, to avoid side effects from proving `Copy` affecting whether repeat counts are known or not.
+            // If we did not do this we would get results that depend on the order that we evaluate each repeat
+            // expr's `Copy` check.
+            .collect::<Vec<_>>();
+
+        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)
+                }
+                // 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.
+                ty::ConstKind::Value(_) => (),
+
+                // If the length is a generic parameter or some rigid alias then conservatively
+                // require `element_ty: Copy` as it may wind up being `>1` after monomorphization.
+                ty::ConstKind::Param(_)
+                | ty::ConstKind::Expr(_)
+                | ty::ConstKind::Placeholder(_)
+                | ty::ConstKind::Unevaluated(_) => {
+                    self.enforce_repeat_element_needs_copy_bound(element, element_ty)
+                }
+
+                ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => {
+                    unreachable!()
+                }
             }
         }
     }
diff --git a/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs
index 4654d7483a6..bf519febb30 100644
--- a/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs
+++ b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs
@@ -1,5 +1,3 @@
-//@ check-pass
-
 #![feature(generic_arg_infer)]
 
 // Test that if we defer repeat expr copy checks to end of typechecking they're
@@ -37,6 +35,7 @@ fn main() {
     let b: [Foo<_>; 2] = [Foo(PhantomData); _];
     tie(&a, b);
     let c = [NotCopy; _];
+    //~^ ERROR: type annotations needed for `[NotCopy; _]`
 
     // a is of type `?y`
     // b is of type `[Foo<?y>; 2]`
diff --git a/tests/ui/repeat-expr/copy-check-deferred-before-fallback.stderr b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.stderr
new file mode 100644
index 00000000000..d6189329c92
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed for `[NotCopy; _]`
+  --> $DIR/copy-check-deferred-before-fallback.rs:37:9
+   |
+LL |     let c = [NotCopy; _];
+   |         ^    ------- type must be known at this point
+   |
+help: consider giving `c` an explicit type, where the value of const parameter `N` is specified
+   |
+LL |     let c: [_; N] = [NotCopy; _];
+   |          ++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/repeat-expr/copy-check-inference-side-effects.rs b/tests/ui/repeat-expr/copy-check-inference-side-effects.rs
new file mode 100644
index 00000000000..416a20169fb
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-inference-side-effects.rs
@@ -0,0 +1,34 @@
+#![feature(generic_arg_infer)]
+
+struct Foo<const N: usize>;
+
+impl Clone for Foo<1> {
+    fn clone(&self) -> Self {
+        Self
+    }
+}
+impl Copy for Foo<1> {}
+
+fn unify<const N: usize>(_: &[Foo<N>; 2], _: &[String; N]) {}
+
+fn works_if_inference_side_effects() {
+    // This will only pass if inference side effectrs from proving `Foo<?x>: Copy` are
+    // able to be relied upon by other repeat expressions.
+    let a /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
+    //~^ ERROR: type annotations needed for `[Foo<_>; 2]`
+    let b /* : [String; ?x] */ = ["string".to_string(); _];
+
+    unify(&a, &b);
+}
+
+fn works_if_fixed_point() {
+    // This will only pass if the *second* array repeat expr is checked first
+    // allowing `Foo<?x>: Copy` to infer the array length of the first repeat expr.
+    let b /* : [String; ?x] */ = ["string".to_string(); _];
+    //~^ ERROR: type annotations needed for `[String; _]`
+    let a /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
+
+    unify(&a, &b);
+}
+
+fn main() {}
diff --git a/tests/ui/repeat-expr/copy-check-inference-side-effects.stderr b/tests/ui/repeat-expr/copy-check-inference-side-effects.stderr
new file mode 100644
index 00000000000..505beff0f6b
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-inference-side-effects.stderr
@@ -0,0 +1,28 @@
+error[E0282]: type annotations needed for `[Foo<_>; 2]`
+  --> $DIR/copy-check-inference-side-effects.rs:17:9
+   |
+LL |     let a /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
+   |         ^
+LL |
+LL |     let b /* : [String; ?x] */ = ["string".to_string(); _];
+   |                                   -------------------- 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: [Foo<N>; 2] /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
+   |          +++++++++++++
+
+error[E0282]: type annotations needed for `[String; _]`
+  --> $DIR/copy-check-inference-side-effects.rs:27:9
+   |
+LL |     let b /* : [String; ?x] */ = ["string".to_string(); _];
+   |         ^                         -------------------- type must be known at this point
+   |
+help: consider giving `b` an explicit type, where the value of const parameter `N` is specified
+   |
+LL |     let b: [_; N] /* : [String; ?x] */ = ["string".to_string(); _];
+   |          ++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0282`.