Skip to content

Commit 7df7c3c

Browse files
committed
Defer repeat expr Copy checks
1 parent 8966d60 commit 7df7c3c

File tree

3 files changed

+59
-14
lines changed

3 files changed

+59
-14
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 40 additions & 11 deletions
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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
123123
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
124124
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
125125
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));
126+
// We want to emit an error if the const is not structurally resolveable as otherwise
127+
// we can find up conservatively proving `Copy` which may infer the repeat expr count
128+
// to something that never required `Copy` in the first place.
129+
let count =
130+
self.structurally_resolve_const(element.span, self.normalize(element.span, count));
131+
132+
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
133+
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
134+
if count.references_error() {
135+
continue;
136+
}
128137

129138
// If the length is 0, we don't create any elements, so we don't copy any.
130139
// 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.
140+
// for `Copy` if the length is larger.
132141
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
133142
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
134143
}

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ fn typeck_with_inspect<'tcx>(
194194
fcx.write_ty(id, expected_type);
195195
};
196196

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

199206
fcx.type_inference_fallback();

0 commit comments

Comments
 (0)