Skip to content

Commit 1c58fc4

Browse files
committed
Don't allow repeat expr count inference side effects to propagate
1 parent 9d28fe3 commit 1c58fc4

File tree

5 files changed

+123
-19
lines changed

5 files changed

+123
-19
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
111111
pub(in super::super) fn check_repeat_exprs(&self) {
112112
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
113113
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
114-
for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
115-
// We want to emit an error if the const is not structurally resolveable as otherwise
116-
// we can find up conservatively proving `Copy` which may infer the repeat expr count
117-
// to something that never required `Copy` in the first place.
118-
let count =
119-
self.structurally_resolve_const(element.span, self.normalize(element.span, count));
120-
121-
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
122-
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
123-
if count.references_error() {
124-
continue;
125-
}
126114

127-
// If the length is 0, we don't create any elements, so we don't copy any.
128-
// If the length is 1, we don't copy that one element, we move it. Only check
129-
// for `Copy` if the length is larger.
130-
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
131-
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
115+
let deferred_repeat_expr_checks = deferred_repeat_expr_checks
116+
.drain(..)
117+
.flat_map(|(element, element_ty, count)| {
118+
// We want to emit an error if the const is not structurally resolveable as otherwise
119+
// we can find up conservatively proving `Copy` which may infer the repeat expr count
120+
// to something that never required `Copy` in the first place.
121+
let count = self
122+
.structurally_resolve_const(element.span, self.normalize(element.span, count));
123+
124+
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
125+
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
126+
if count.references_error() {
127+
return None;
128+
}
129+
130+
Some((element, element_ty, count))
131+
})
132+
// We collect to force the side effects of structurally resolving the repeat count to happen in one
133+
// go, to avoid side effects from proving `Copy` affecting whether repeat counts are known or not.
134+
// If we did not do this we would get results that depend on the order that we evaluate each repeat
135+
// expr's `Copy` check.
136+
.collect::<Vec<_>>();
137+
138+
for (element, element_ty, count) in deferred_repeat_expr_checks {
139+
match count.kind() {
140+
ty::ConstKind::Value(val)
141+
if val.try_to_target_usize(self.tcx).is_none_or(|count| count > 1) =>
142+
{
143+
self.enforce_repeat_element_needs_copy_bound(element, element_ty)
144+
}
145+
// If the length is 0 or 1 we don't actually copy the element, we either don't create it
146+
// or we just use the one value.
147+
ty::ConstKind::Value(_) => (),
148+
149+
// If the length is a generic parameter or some rigid alias then conservatively
150+
// require `element_ty: Copy` as it may wind up being `>1` after monomorphization.
151+
ty::ConstKind::Param(_)
152+
| ty::ConstKind::Expr(_)
153+
| ty::ConstKind::Placeholder(_)
154+
| ty::ConstKind::Unevaluated(_) => {
155+
self.enforce_repeat_element_needs_copy_bound(element, element_ty)
156+
}
157+
158+
ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => {
159+
unreachable!()
160+
}
132161
}
133162
}
134163
}

tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//@ check-pass
2-
31
#![feature(generic_arg_infer)]
42

53
// Test that if we defer repeat expr copy checks to end of typechecking they're
@@ -37,6 +35,7 @@ fn main() {
3735
let b: [Foo<_>; 2] = [Foo(PhantomData); _];
3836
tie(&a, b);
3937
let c = [NotCopy; _];
38+
//~^ ERROR: type annotations needed for `[NotCopy; _]`
4039

4140
// a is of type `?y`
4241
// b is of type `[Foo<?y>; 2]`
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0282]: type annotations needed for `[NotCopy; _]`
2+
--> $DIR/copy-check-deferred-before-fallback.rs:37:9
3+
|
4+
LL | let c = [NotCopy; _];
5+
| ^ ------- type must be known at this point
6+
|
7+
help: consider giving `c` an explicit type, where the value of const parameter `N` is specified
8+
|
9+
LL | let c: [_; N] = [NotCopy; _];
10+
| ++++++++
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0282`.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#![feature(generic_arg_infer)]
2+
3+
struct Foo<const N: usize>;
4+
5+
impl Clone for Foo<1> {
6+
fn clone(&self) -> Self {
7+
Self
8+
}
9+
}
10+
impl Copy for Foo<1> {}
11+
12+
fn unify<const N: usize>(_: &[Foo<N>; 2], _: &[String; N]) {}
13+
14+
fn works_if_inference_side_effects() {
15+
// This will only pass if inference side effectrs from proving `Foo<?x>: Copy` are
16+
// able to be relied upon by other repeat expressions.
17+
let a /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
18+
//~^ ERROR: type annotations needed for `[Foo<_>; 2]`
19+
let b /* : [String; ?x] */ = ["string".to_string(); _];
20+
21+
unify(&a, &b);
22+
}
23+
24+
fn works_if_fixed_point() {
25+
// This will only pass if the *second* array repeat expr is checked first
26+
// allowing `Foo<?x>: Copy` to infer the array length of the first repeat expr.
27+
let b /* : [String; ?x] */ = ["string".to_string(); _];
28+
//~^ ERROR: type annotations needed for `[String; _]`
29+
let a /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
30+
31+
unify(&a, &b);
32+
}
33+
34+
fn main() {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error[E0282]: type annotations needed for `[Foo<_>; 2]`
2+
--> $DIR/copy-check-inference-side-effects.rs:17:9
3+
|
4+
LL | let a /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
5+
| ^
6+
LL |
7+
LL | let b /* : [String; ?x] */ = ["string".to_string(); _];
8+
| -------------------- type must be known at this point
9+
|
10+
help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
11+
|
12+
LL | let a: [Foo<N>; 2] /* : [Foo<?x>; 2] */ = [Foo::<_>; 2];
13+
| +++++++++++++
14+
15+
error[E0282]: type annotations needed for `[String; _]`
16+
--> $DIR/copy-check-inference-side-effects.rs:27:9
17+
|
18+
LL | let b /* : [String; ?x] */ = ["string".to_string(); _];
19+
| ^ -------------------- type must be known at this point
20+
|
21+
help: consider giving `b` an explicit type, where the value of const parameter `N` is specified
22+
|
23+
LL | let b: [_; N] /* : [String; ?x] */ = ["string".to_string(); _];
24+
| ++++++++
25+
26+
error: aborting due to 2 previous errors
27+
28+
For more information about this error, try `rustc --explain E0282`.

0 commit comments

Comments
 (0)