Skip to content

Commit 330a860

Browse files
authored
Unrolled build for rust-lang#139635
Rollup merge of rust-lang#139635 - BoxyUwU:no_order_dependent_copy_checks, r=lcnr Finalize repeat expr inference behaviour with inferred repeat counts I believe this should be the last change of how repeat exprs are handled before it's finished for `generic_arg_infer`. Assuming we don't wind up deciding to replace this all with a new predicate kind :) This PR has three actual changes: - Always defer the checks to end of typeck even when generic arg infer is not enabled (needs an FCP) - Properly handle element exprs that are constants when the repeat count is inferred - "Isolate" each repeat expr check so that inference constraints from `Copy` goals dont affect other repeat expr checks resulting in weird order-dependent inference The commit history and tests should be relatively helpful for understanding this PR's impl. r? compiler-errors
2 parents 2eef478 + cc10370 commit 330a860

14 files changed

+361
-153
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,62 +1900,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19001900
// We defer checking whether the element type is `Copy` as it is possible to have
19011901
// an inference variable as a repeat count and it seems unlikely that `Copy` would
19021902
// have inference side effects required for type checking to succeed.
1903-
if tcx.features().generic_arg_infer() {
1904-
self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
1905-
// If the length is 0, we don't create any elements, so we don't copy any.
1906-
// If the length is 1, we don't copy that one element, we move it. Only check
1907-
// for `Copy` if the length is larger, or unevaluated.
1908-
} else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
1909-
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
1910-
}
1903+
self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
19111904

19121905
let ty = Ty::new_array_with_const_len(tcx, t, count);
19131906
self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None));
19141907
ty
19151908
}
19161909

1917-
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
1918-
pub(super) fn enforce_repeat_element_needs_copy_bound(
1919-
&self,
1920-
element: &hir::Expr<'_>,
1921-
element_ty: Ty<'tcx>,
1922-
) {
1923-
let tcx = self.tcx;
1924-
// Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
1925-
match &element.kind {
1926-
hir::ExprKind::ConstBlock(..) => return,
1927-
hir::ExprKind::Path(qpath) => {
1928-
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
1929-
if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
1930-
{
1931-
return;
1932-
}
1933-
}
1934-
_ => {}
1935-
}
1936-
// If someone calls a const fn or constructs a const value, they can extract that
1937-
// out into a separate constant (or a const block in the future), so we check that
1938-
// to tell them that in the diagnostic. Does not affect typeck.
1939-
let is_constable = match element.kind {
1940-
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
1941-
ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn,
1942-
_ => traits::IsConstable::No,
1943-
},
1944-
hir::ExprKind::Path(qpath) => {
1945-
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
1946-
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
1947-
_ => traits::IsConstable::No,
1948-
}
1949-
}
1950-
_ => traits::IsConstable::No,
1951-
};
1952-
1953-
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
1954-
let code =
1955-
traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span };
1956-
self.require_type_meets(element_ty, element.span, code, lang_item);
1957-
}
1958-
19591910
fn check_expr_tuple(
19601911
&self,
19611912
elts: &'tcx [hir::Expr<'tcx>],

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use itertools::Itertools;
44
use rustc_data_structures::fx::FxIndexSet;
55
use rustc_errors::codes::*;
66
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize};
7-
use rustc_hir::def::{CtorOf, DefKind, Res};
7+
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
88
use rustc_hir::def_id::DefId;
99
use rustc_hir::intravisit::Visitor;
10-
use rustc_hir::{ExprKind, HirId, Node, QPath};
10+
use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath};
1111
use rustc_hir_analysis::check::potentially_plural_count;
1212
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
1313
use rustc_index::IndexVec;
@@ -104,24 +104,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
104104
pub(in super::super) fn check_repeat_exprs(&self) {
105105
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
106106
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
107-
for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
108-
// We want to emit an error if the const is not structurally resolveable as otherwise
109-
// we can find up conservatively proving `Copy` which may infer the repeat expr count
110-
// to something that never required `Copy` in the first place.
111-
let count =
112-
self.structurally_resolve_const(element.span, self.normalize(element.span, count));
113-
114-
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
115-
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
116-
if count.references_error() {
117-
continue;
118-
}
119107

120-
// If the length is 0, we don't create any elements, so we don't copy any.
121-
// If the length is 1, we don't copy that one element, we move it. Only check
122-
// for `Copy` if the length is larger.
123-
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
124-
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
108+
let deferred_repeat_expr_checks = deferred_repeat_expr_checks
109+
.drain(..)
110+
.flat_map(|(element, element_ty, count)| {
111+
// Actual constants as the repeat element are inserted repeatedly instead
112+
// of being copied via `Copy`, so we don't need to attempt to structurally
113+
// resolve the repeat count which may unnecessarily error.
114+
match &element.kind {
115+
hir::ExprKind::ConstBlock(..) => return None,
116+
hir::ExprKind::Path(qpath) => {
117+
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
118+
if let Res::Def(DefKind::Const | DefKind::AssocConst, _) = res {
119+
return None;
120+
}
121+
}
122+
_ => {}
123+
}
124+
125+
// We want to emit an error if the const is not structurally resolveable
126+
// as otherwise we can wind up conservatively proving `Copy` which may
127+
// infer the repeat expr count to something that never required `Copy` in
128+
// the first place.
129+
let count = self
130+
.structurally_resolve_const(element.span, self.normalize(element.span, count));
131+
132+
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the
133+
// repeat expr count is erroneous/unknown. The user might wind up
134+
// specifying a repeat count of 0/1.
135+
if count.references_error() {
136+
return None;
137+
}
138+
139+
Some((element, element_ty, count))
140+
})
141+
// We collect to force the side effects of structurally resolving the repeat
142+
// count to happen in one go, to avoid side effects from proving `Copy`
143+
// affecting whether repeat counts are known or not. If we did not do this we
144+
// would get results that depend on the order that we evaluate each repeat
145+
// expr's `Copy` check.
146+
.collect::<Vec<_>>();
147+
148+
let enforce_copy_bound = |element: &hir::Expr<'_>, element_ty| {
149+
// If someone calls a const fn or constructs a const value, they can extract that
150+
// out into a separate constant (or a const block in the future), so we check that
151+
// to tell them that in the diagnostic. Does not affect typeck.
152+
let is_constable = match element.kind {
153+
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
154+
ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => {
155+
traits::IsConstable::Fn
156+
}
157+
_ => traits::IsConstable::No,
158+
},
159+
hir::ExprKind::Path(qpath) => {
160+
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
161+
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
162+
_ => traits::IsConstable::No,
163+
}
164+
}
165+
_ => traits::IsConstable::No,
166+
};
167+
168+
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
169+
let code = traits::ObligationCauseCode::RepeatElementCopy {
170+
is_constable,
171+
elt_span: element.span,
172+
};
173+
self.require_type_meets(element_ty, element.span, code, lang_item);
174+
};
175+
176+
for (element, element_ty, count) in deferred_repeat_expr_checks {
177+
match count.kind() {
178+
ty::ConstKind::Value(val) => {
179+
if val.try_to_target_usize(self.tcx).is_none_or(|count| count > 1) {
180+
enforce_copy_bound(element, element_ty)
181+
} else {
182+
// If the length is 0 or 1 we don't actually copy the element, we either don't create it
183+
// or we just use the one value.
184+
}
185+
}
186+
187+
// If the length is a generic parameter or some rigid alias then conservatively
188+
// require `element_ty: Copy` as it may wind up being `>1` after monomorphization.
189+
ty::ConstKind::Param(_)
190+
| ty::ConstKind::Expr(_)
191+
| ty::ConstKind::Placeholder(_)
192+
| ty::ConstKind::Unevaluated(_) => enforce_copy_bound(element, element_ty),
193+
194+
ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => {
195+
unreachable!()
196+
}
125197
}
126198
}
127199
}

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,16 @@ fn typeck_with_inspect<'tcx>(
196196
fcx.write_ty(id, expected_type);
197197
};
198198

199-
// Whether to check repeat exprs before/after inference fallback is somewhat arbitrary of a decision
200-
// as neither option is strictly more permissive than the other. However, we opt to check repeat exprs
201-
// first as errors from not having inferred array lengths yet seem less confusing than errors from inference
202-
// fallback arbitrarily inferring something incompatible with `Copy` inference side effects.
199+
// Whether to check repeat exprs before/after inference fallback is somewhat
200+
// arbitrary of a decision as neither option is strictly more permissive than
201+
// the other. However, we opt to check repeat exprs first as errors from not
202+
// having inferred array lengths yet seem less confusing than errors from inference
203+
// fallback arbitrarily inferring something incompatible with `Copy` inference
204+
// side effects.
203205
//
204-
// This should also be forwards compatible with moving repeat expr checks to a custom goal kind or using
205-
// marker traits in the future.
206+
// FIXME(#140855): This should also be forwards compatible with moving
207+
// repeat expr checks to a custom goal kind or using marker traits in
208+
// the future.
206209
fcx.check_repeat_exprs();
207210

208211
fcx.type_inference_fallback();

tests/ui/lang-items/lang-item-generic-requirements.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,14 @@ fn ice() {
4949
// Use index
5050
let arr = [0; 5];
5151
let _ = arr[2];
52+
//~^ ERROR cannot index into a value of type `[{integer}; 5]`
5253

5354
// Use phantomdata
5455
let _ = MyPhantomData::<(), i32>;
5556

5657
// Use Foo
5758
let _: () = Foo;
59+
//~^ ERROR mismatched types
5860
}
5961

6062
// use `start`

tests/ui/lang-items/lang-item-generic-requirements.stderr

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,23 @@ LL | r + a;
7676
| |
7777
| {integer}
7878

79+
error[E0608]: cannot index into a value of type `[{integer}; 5]`
80+
--> $DIR/lang-item-generic-requirements.rs:51:16
81+
|
82+
LL | let _ = arr[2];
83+
| ^^^
84+
85+
error[E0308]: mismatched types
86+
--> $DIR/lang-item-generic-requirements.rs:58:17
87+
|
88+
LL | let _: () = Foo;
89+
| -- ^^^ expected `()`, found `Foo`
90+
| |
91+
| expected due to this
92+
7993
error: requires `copy` lang_item
8094

81-
error: aborting due to 10 previous errors
95+
error: aborting due to 12 previous errors
8296

83-
Some errors have detailed explanations: E0369, E0392, E0718.
84-
For more information about an error, try `rustc --explain E0369`.
97+
Some errors have detailed explanations: E0308, E0369, E0392, E0608, E0718.
98+
For more information about an error, try `rustc --explain E0308`.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#![feature(generic_arg_infer)]
2+
3+
// Test when deferring repeat expr copy checks to end of typechecking whether elements
4+
// that are const items allow for repeat counts to go uninferred without an error being
5+
// emitted if they would later wind up inferred by integer fallback.
6+
//
7+
// This test should be updated if we wind up deferring repeat expr checks until *after*
8+
// integer fallback as the point of the test is not *specifically* about integer fallback
9+
// but rather about the behaviour of `const` element exprs.
10+
11+
trait Trait<const N: usize> {}
12+
13+
// We impl `Trait` for both `i32` and `u32` to avoid being able
14+
// to prove `?int: Trait<?n>` from there only being one impl.
15+
impl Trait<2> for i32 {}
16+
impl Trait<2> for u32 {}
17+
18+
fn tie_and_make_goal<const N: usize, T: Trait<N>>(_: &T, _: &[String; N]) {}
19+
20+
fn const_block() {
21+
// Deferred repeat expr `String; ?n`
22+
let a = [const { String::new() }; _];
23+
24+
// `?int: Trait<?n>` goal
25+
tie_and_make_goal(&1, &a);
26+
27+
// If repeat expr checks structurally resolve the `?n`s before checking if the
28+
// element is a `const` then we would error here. Otherwise we avoid doing so,
29+
// integer fallback occurs, allowing `?int: Trait<?n>` goals to make progress,
30+
// inferring the repeat counts (to `2` but that doesn't matter as the element is `const`).
31+
}
32+
33+
fn const_item() {
34+
const MY_CONST: String = String::new();
35+
36+
// Deferred repeat expr `String; ?n`
37+
let a = [MY_CONST; _];
38+
39+
// `?int: Trait<?n>` goal
40+
tie_and_make_goal(&1, &a);
41+
42+
// ... same as `const_block`
43+
}
44+
45+
fn assoc_const() {
46+
trait Dummy {
47+
const ASSOC: String;
48+
}
49+
impl Dummy for () {
50+
const ASSOC: String = String::new();
51+
}
52+
53+
// Deferred repeat expr `String; ?n`
54+
let a = [<() as Dummy>::ASSOC; _];
55+
56+
// `?int: Trait<?n>` goal
57+
tie_and_make_goal(&1, &a);
58+
59+
// ... same as `const_block`
60+
}
61+
62+
fn const_block_but_uninferred() {
63+
// Deferred repeat expr `String; ?n`
64+
let a = [const { String::new() }; _];
65+
//~^ ERROR: type annotations needed for `[String; _]`
66+
67+
// Even if we don't structurally resolve the repeat count as part of repeat expr
68+
// checks, we still error on the repeat count being uninferred as we require all
69+
// types/consts to be inferred by the end of type checking.
70+
}
71+
72+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0284]: type annotations needed for `[String; _]`
2+
--> $DIR/copy-check-const-element-uninferred-count.rs:64:9
3+
|
4+
LL | let a = [const { String::new() }; _];
5+
| ^ ---------------------------- type must be known at this point
6+
|
7+
= note: the length of array `[String; _]` must be type `usize`
8+
help: consider giving `a` an explicit type, where the placeholders `_` are specified
9+
|
10+
LL | let a: [_; _] = [const { String::new() }; _];
11+
| ++++++++
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0284`.

0 commit comments

Comments
 (0)