Skip to content

Commit 9053a8a

Browse files
committed
Auto merge of #137045 - BoxyUwU:defer_repeat_expr_checks, r=<try>
Defer repeat expr `Copy` checks to end of type checking Fixes #110443 r? `@ghost`
2 parents d8810e3 + c09f183 commit 9053a8a

File tree

10 files changed

+135
-59
lines changed

10 files changed

+135
-59
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+4-50
Original file line numberDiff line numberDiff line change
@@ -1856,62 +1856,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18561856
return Ty::new_error(tcx, guar);
18571857
}
18581858

1859-
// If the length is 0, we don't create any elements, so we don't copy any.
1860-
// If the length is 1, we don't copy that one element, we move it. Only check
1861-
// for `Copy` if the length is larger, or unevaluated.
1862-
// FIXME(min_const_generic_exprs): We could perhaps defer this check so that
1863-
// we don't require `<?0t as Tr>::CONST` doesn't unnecessarily require `Copy`.
1864-
if count.try_to_target_usize(tcx).is_none_or(|x| x > 1) {
1865-
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
1866-
}
1859+
// We defer checking whether the element type is `Copy` as it is possible to have
1860+
// an inference variable as a repeat count and it seems unlikely that `Copy` would
1861+
// have inference side effects required for type checking to succeed.
1862+
self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
18671863

18681864
let ty = Ty::new_array_with_const_len(tcx, t, count);
18691865
self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None));
18701866
ty
18711867
}
18721868

1873-
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
1874-
fn enforce_repeat_element_needs_copy_bound(
1875-
&self,
1876-
element: &hir::Expr<'_>,
1877-
element_ty: Ty<'tcx>,
1878-
) {
1879-
let tcx = self.tcx;
1880-
// Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
1881-
match &element.kind {
1882-
hir::ExprKind::ConstBlock(..) => return,
1883-
hir::ExprKind::Path(qpath) => {
1884-
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
1885-
if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
1886-
{
1887-
return;
1888-
}
1889-
}
1890-
_ => {}
1891-
}
1892-
// If someone calls a const fn or constructs a const value, they can extract that
1893-
// out into a separate constant (or a const block in the future), so we check that
1894-
// to tell them that in the diagnostic. Does not affect typeck.
1895-
let is_constable = match element.kind {
1896-
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
1897-
ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn,
1898-
_ => traits::IsConstable::No,
1899-
},
1900-
hir::ExprKind::Path(qpath) => {
1901-
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
1902-
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
1903-
_ => traits::IsConstable::No,
1904-
}
1905-
}
1906-
_ => traits::IsConstable::No,
1907-
};
1908-
1909-
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
1910-
let code =
1911-
traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span };
1912-
self.require_type_meets(element_ty, element.span, code, lang_item);
1913-
}
1914-
19151869
fn check_expr_tuple(
19161870
&self,
19171871
elts: &'tcx [hir::Expr<'tcx>],

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+60-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use rustc_errors::codes::*;
66
use rustc_errors::{
77
Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, a_or_an, listify, pluralize,
88
};
9-
use rustc_hir::def::{CtorOf, DefKind, Res};
9+
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
1010
use rustc_hir::def_id::DefId;
1111
use rustc_hir::intravisit::Visitor;
12-
use rustc_hir::{ExprKind, HirId, Node, QPath};
12+
use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath};
1313
use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
1414
use rustc_hir_analysis::check::potentially_plural_count;
1515
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
@@ -119,6 +119,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
119119
}
120120
}
121121

122+
pub(in super::super) fn check_repeat_exprs(&self) {
123+
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
124+
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
125+
for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
126+
let count = self
127+
.try_structurally_resolve_const(element.span, self.normalize(element.span, count));
128+
129+
// If the length is 0, we don't create any elements, so we don't copy any.
130+
// If the length is 1, we don't copy that one element, we move it. Only check
131+
// for `Copy` if the length is larger, or unevaluated.
132+
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
133+
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
134+
}
135+
}
136+
}
137+
138+
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
139+
fn enforce_repeat_element_needs_copy_bound(
140+
&self,
141+
element: &hir::Expr<'_>,
142+
element_ty: Ty<'tcx>,
143+
) {
144+
let tcx = self.tcx;
145+
// Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
146+
match &element.kind {
147+
hir::ExprKind::ConstBlock(..) => return,
148+
hir::ExprKind::Path(qpath) => {
149+
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
150+
if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
151+
{
152+
return;
153+
}
154+
}
155+
_ => {}
156+
}
157+
// If someone calls a const fn or constructs a const value, they can extract that
158+
// out into a separate constant (or a const block in the future), so we check that
159+
// to tell them that in the diagnostic. Does not affect typeck.
160+
let is_constable = match element.kind {
161+
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
162+
ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn,
163+
_ => traits::IsConstable::No,
164+
},
165+
hir::ExprKind::Path(qpath) => {
166+
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
167+
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
168+
_ => traits::IsConstable::No,
169+
}
170+
}
171+
_ => traits::IsConstable::No,
172+
};
173+
174+
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
175+
let code =
176+
traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span };
177+
self.require_type_meets(element_ty, element.span, code, lang_item);
178+
}
179+
122180
pub(in super::super) fn check_method_argument_types(
123181
&self,
124182
sp: Span,

compiler/rustc_hir_typeck/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ fn typeck_with_inspect<'tcx>(
194194
fcx.write_ty(id, expected_type);
195195
};
196196

197+
fcx.check_repeat_exprs();
198+
197199
fcx.type_inference_fallback();
198200

199201
// Even though coercion casts provide type hints, we check casts after fallback for

compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs

+4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
6262

6363
pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>)>>,
6464

65+
pub(super) deferred_repeat_expr_checks:
66+
RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
67+
6568
/// Whenever we introduce an adjustment from `!` into a type variable,
6669
/// we record that type variable here. This is later used to inform
6770
/// fallback. See the `fallback` module for details.
@@ -96,6 +99,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
9699
deferred_transmute_checks: RefCell::new(Vec::new()),
97100
deferred_asm_checks: RefCell::new(Vec::new()),
98101
deferred_coroutine_interiors: RefCell::new(Vec::new()),
102+
deferred_repeat_expr_checks: RefCell::new(Vec::new()),
99103
diverging_type_vars: RefCell::new(Default::default()),
100104
infer_var_info: RefCell::new(Default::default()),
101105
}

tests/ui/consts/const-fn-in-vec.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
static _MAYBE_STRINGS: [Option<String>; 5] = [None; 5];
22
//~^ ERROR the trait bound `String: Copy` is not satisfied
33

4-
fn main() {
5-
// should hint to create an inline `const` block
6-
// or to create a new `const` item
4+
// should hint to create an inline `const` block
5+
// or to create a new `const` item
6+
fn foo() {
77
let _strings: [String; 5] = [String::new(); 5];
88
//~^ ERROR the trait bound `String: Copy` is not satisfied
9+
}
10+
11+
fn bar() {
912
let _maybe_strings: [Option<String>; 5] = [None; 5];
1013
//~^ ERROR the trait bound `String: Copy` is not satisfied
1114
}
15+
16+
fn main() {}

tests/ui/consts/const-fn-in-vec.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ LL | let _strings: [String; 5] = [String::new(); 5];
2222
= note: the `Copy` trait is required because this value will be copied for each element of the array
2323

2424
error[E0277]: the trait bound `String: Copy` is not satisfied
25-
--> $DIR/const-fn-in-vec.rs:9:48
25+
--> $DIR/const-fn-in-vec.rs:12:48
2626
|
2727
LL | let _maybe_strings: [Option<String>; 5] = [None; 5];
2828
| ^^^^

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

+2
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
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

+17-3
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`.

tests/ui/repeat-expr/infer-eager.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use std::marker::PhantomData;
2+
3+
struct Foo<T>(PhantomData<T>);
4+
5+
impl Clone for Foo<u8> {
6+
fn clone(&self) -> Self {
7+
Foo(PhantomData)
8+
}
9+
}
10+
impl Copy for Foo<u8> {}
11+
12+
fn extract<T, const N: usize>(_: [Foo<T>; N]) -> T {
13+
loop {}
14+
}
15+
16+
fn main() {
17+
let x = [Foo(PhantomData); 2];
18+
//~^ ERROR: type annotations needed
19+
_ = extract(x).max(2);
20+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0282]: type annotations needed for `[Foo<_>; 2]`
2+
--> $DIR/infer-eager.rs:17:9
3+
|
4+
LL | let x = [Foo(PhantomData); 2];
5+
| ^
6+
LL |
7+
LL | _ = extract(x).max(2);
8+
| ---------- type must be known at this point
9+
|
10+
help: consider giving `x` an explicit type, where the type for type parameter `T` is specified
11+
|
12+
LL | let x: [Foo<T>; 2] = [Foo(PhantomData); 2];
13+
| +++++++++++++
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0282`.

0 commit comments

Comments
 (0)