Skip to content

Commit e47d32e

Browse files
committed
Defer repeat expr Copy check
1 parent 53a31bb commit e47d32e

File tree

4 files changed

+70
-52
lines changed

4 files changed

+70
-52
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
}

0 commit comments

Comments
 (0)