Skip to content

Commit dc6db19

Browse files
committed
Defer repeat expr Copy check
1 parent cdd8895 commit dc6db19

File tree

5 files changed

+85
-15
lines changed

5 files changed

+85
-15
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1853,12 +1853,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18531853
return Ty::new_error(tcx, guar);
18541854
}
18551855

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

@@ -1868,7 +1871,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18681871
}
18691872

18701873
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
1871-
fn enforce_repeat_element_needs_copy_bound(
1874+
pub(super) fn enforce_repeat_element_needs_copy_bound(
18721875
&self,
18731876
element: &hir::Expr<'_>,
18741877
element_ty: Ty<'tcx>,

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+40-11
Original file line numberDiff line numberDiff line change
@@ -85,33 +85,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8585
})
8686
}
8787

88-
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
88+
/// Resolves type and const variables in `t` if possible. Unlike the infcx
8989
/// version (resolve_vars_if_possible), this version will
9090
/// also select obligations if it seems useful, in an effort
9191
/// to get more type information.
9292
// FIXME(-Znext-solver): A lot of the calls to this method should
9393
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
9494
#[instrument(skip(self), level = "debug", ret)]
95-
pub(crate) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
95+
pub(crate) fn resolve_vars_with_obligations<T: TypeFoldable<TyCtxt<'tcx>>>(
96+
&self,
97+
mut t: T,
98+
) -> T {
9699
// No Infer()? Nothing needs doing.
97-
if !ty.has_non_region_infer() {
100+
if !t.has_non_region_infer() {
98101
debug!("no inference var, nothing needs doing");
99-
return ty;
102+
return t;
100103
}
101104

102-
// If `ty` is a type variable, see whether we already know what it is.
103-
ty = self.resolve_vars_if_possible(ty);
104-
if !ty.has_non_region_infer() {
105-
debug!(?ty);
106-
return ty;
105+
// If `t` is a type variable, see whether we already know what it is.
106+
t = self.resolve_vars_if_possible(t);
107+
if !t.has_non_region_infer() {
108+
debug!(?t);
109+
return t;
107110
}
108111

109112
// If not, try resolving pending obligations as much as
110113
// possible. This can help substantially when there are
111114
// indirect dependencies that don't seem worth tracking
112115
// precisely.
113116
self.select_obligations_where_possible(|_| {});
114-
self.resolve_vars_if_possible(ty)
117+
self.resolve_vars_if_possible(t)
115118
}
116119

117120
pub(crate) fn record_deferred_call_resolution(
@@ -1454,7 +1457,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14541457
sp: Span,
14551458
ct: ty::Const<'tcx>,
14561459
) -> ty::Const<'tcx> {
1457-
// FIXME(min_const_generic_exprs): We could process obligations here if `ct` is a var.
1460+
let ct = self.resolve_vars_with_obligations(ct);
14581461

14591462
if self.next_trait_solver()
14601463
&& let ty::ConstKind::Unevaluated(..) = ct.kind()
@@ -1510,6 +1513,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15101513
}
15111514
}
15121515

1516+
pub(crate) fn structurally_resolve_const(
1517+
&self,
1518+
sp: Span,
1519+
ct: ty::Const<'tcx>,
1520+
) -> ty::Const<'tcx> {
1521+
let ct = self.try_structurally_resolve_const(sp, ct);
1522+
1523+
if !ct.is_ct_infer() {
1524+
ct
1525+
} else {
1526+
let e = self.tainted_by_errors().unwrap_or_else(|| {
1527+
self.err_ctxt()
1528+
.emit_inference_failure_err(
1529+
self.body_id,
1530+
sp,
1531+
ct.into(),
1532+
TypeAnnotationNeeded::E0282,
1533+
true,
1534+
)
1535+
.emit()
1536+
});
1537+
// FIXME: Infer `?ct = {const error}`?
1538+
ty::Const::new_error(self.tcx, e)
1539+
}
1540+
}
1541+
15131542
pub(crate) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
15141543
&self,
15151544
id: HirId,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+25
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
115115
}
116116
}
117117

118+
pub(in super::super) fn check_repeat_exprs(&self) {
119+
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
120+
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
121+
for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
122+
// We want to emit an error if the const is not structurally resolveable as otherwise
123+
// we can find up conservatively proving `Copy` which may infer the repeat expr count
124+
// to something that never required `Copy` in the first place.
125+
let count =
126+
self.structurally_resolve_const(element.span, self.normalize(element.span, count));
127+
128+
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
129+
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
130+
if count.references_error() {
131+
continue;
132+
}
133+
134+
// If the length is 0, we don't create any elements, so we don't copy any.
135+
// If the length is 1, we don't copy that one element, we move it. Only check
136+
// for `Copy` if the length is larger.
137+
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
138+
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
139+
}
140+
}
141+
}
142+
118143
pub(in super::super) fn check_method_argument_types(
119144
&self,
120145
sp: Span,

compiler/rustc_hir_typeck/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,15 @@ fn typeck_with_inspect<'tcx>(
199199
fcx.write_ty(id, expected_type);
200200
};
201201

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

204213
// 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)