Defer repeat expr Copy
check
This commit is contained in:
parent
cdd8895d72
commit
dc6db192c4
5 changed files with 85 additions and 15 deletions
|
@ -1853,12 +1853,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
return Ty::new_error(tcx, guar);
|
return Ty::new_error(tcx, guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We defer checking whether the element type is `Copy` as it is possible to have
|
||||||
|
// an inference variable as a repeat count and it seems unlikely that `Copy` would
|
||||||
|
// have inference side effects required for type checking to succeed.
|
||||||
|
if tcx.features().generic_arg_infer() {
|
||||||
|
self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
|
||||||
// If the length is 0, we don't create any elements, so we don't copy any.
|
// If the length is 0, we don't create any elements, so we don't copy any.
|
||||||
// If the length is 1, we don't copy that one element, we move it. Only check
|
// If the length is 1, we don't copy that one element, we move it. Only check
|
||||||
// for `Copy` if the length is larger, or unevaluated.
|
// for `Copy` if the length is larger, or unevaluated.
|
||||||
// FIXME(min_const_generic_exprs): We could perhaps defer this check so that
|
} else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
|
||||||
// we don't require `<?0t as Tr>::CONST` doesn't unnecessarily require `Copy`.
|
|
||||||
if count.try_to_target_usize(tcx).is_none_or(|x| x > 1) {
|
|
||||||
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
|
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1868,7 +1871,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
|
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
|
||||||
fn enforce_repeat_element_needs_copy_bound(
|
pub(super) fn enforce_repeat_element_needs_copy_bound(
|
||||||
&self,
|
&self,
|
||||||
element: &hir::Expr<'_>,
|
element: &hir::Expr<'_>,
|
||||||
element_ty: Ty<'tcx>,
|
element_ty: Ty<'tcx>,
|
||||||
|
|
|
@ -85,25 +85,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
|
/// Resolves type and const variables in `t` if possible. Unlike the infcx
|
||||||
/// version (resolve_vars_if_possible), this version will
|
/// version (resolve_vars_if_possible), this version will
|
||||||
/// also select obligations if it seems useful, in an effort
|
/// also select obligations if it seems useful, in an effort
|
||||||
/// to get more type information.
|
/// to get more type information.
|
||||||
// FIXME(-Znext-solver): A lot of the calls to this method should
|
// FIXME(-Znext-solver): A lot of the calls to this method should
|
||||||
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
|
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
|
||||||
#[instrument(skip(self), level = "debug", ret)]
|
#[instrument(skip(self), level = "debug", ret)]
|
||||||
pub(crate) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
|
pub(crate) fn resolve_vars_with_obligations<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||||
|
&self,
|
||||||
|
mut t: T,
|
||||||
|
) -> T {
|
||||||
// No Infer()? Nothing needs doing.
|
// No Infer()? Nothing needs doing.
|
||||||
if !ty.has_non_region_infer() {
|
if !t.has_non_region_infer() {
|
||||||
debug!("no inference var, nothing needs doing");
|
debug!("no inference var, nothing needs doing");
|
||||||
return ty;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If `ty` is a type variable, see whether we already know what it is.
|
// If `t` is a type variable, see whether we already know what it is.
|
||||||
ty = self.resolve_vars_if_possible(ty);
|
t = self.resolve_vars_if_possible(t);
|
||||||
if !ty.has_non_region_infer() {
|
if !t.has_non_region_infer() {
|
||||||
debug!(?ty);
|
debug!(?t);
|
||||||
return ty;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not, try resolving pending obligations as much as
|
// If not, try resolving pending obligations as much as
|
||||||
|
@ -111,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// indirect dependencies that don't seem worth tracking
|
// indirect dependencies that don't seem worth tracking
|
||||||
// precisely.
|
// precisely.
|
||||||
self.select_obligations_where_possible(|_| {});
|
self.select_obligations_where_possible(|_| {});
|
||||||
self.resolve_vars_if_possible(ty)
|
self.resolve_vars_if_possible(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn record_deferred_call_resolution(
|
pub(crate) fn record_deferred_call_resolution(
|
||||||
|
@ -1454,7 +1457,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
sp: Span,
|
sp: Span,
|
||||||
ct: ty::Const<'tcx>,
|
ct: ty::Const<'tcx>,
|
||||||
) -> ty::Const<'tcx> {
|
) -> ty::Const<'tcx> {
|
||||||
// FIXME(min_const_generic_exprs): We could process obligations here if `ct` is a var.
|
let ct = self.resolve_vars_with_obligations(ct);
|
||||||
|
|
||||||
if self.next_trait_solver()
|
if self.next_trait_solver()
|
||||||
&& let ty::ConstKind::Unevaluated(..) = ct.kind()
|
&& let ty::ConstKind::Unevaluated(..) = ct.kind()
|
||||||
|
@ -1510,6 +1513,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn structurally_resolve_const(
|
||||||
|
&self,
|
||||||
|
sp: Span,
|
||||||
|
ct: ty::Const<'tcx>,
|
||||||
|
) -> ty::Const<'tcx> {
|
||||||
|
let ct = self.try_structurally_resolve_const(sp, ct);
|
||||||
|
|
||||||
|
if !ct.is_ct_infer() {
|
||||||
|
ct
|
||||||
|
} else {
|
||||||
|
let e = self.tainted_by_errors().unwrap_or_else(|| {
|
||||||
|
self.err_ctxt()
|
||||||
|
.emit_inference_failure_err(
|
||||||
|
self.body_id,
|
||||||
|
sp,
|
||||||
|
ct.into(),
|
||||||
|
TypeAnnotationNeeded::E0282,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.emit()
|
||||||
|
});
|
||||||
|
// FIXME: Infer `?ct = {const error}`?
|
||||||
|
ty::Const::new_error(self.tcx, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
|
pub(crate) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
|
||||||
&self,
|
&self,
|
||||||
id: HirId,
|
id: HirId,
|
||||||
|
|
|
@ -115,6 +115,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in super::super) fn check_repeat_exprs(&self) {
|
||||||
|
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
|
||||||
|
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
|
||||||
|
for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
|
||||||
|
// We want to emit an error if the const is not structurally resolveable as otherwise
|
||||||
|
// we can find up conservatively proving `Copy` which may infer the repeat expr count
|
||||||
|
// to something that never required `Copy` in the first place.
|
||||||
|
let count =
|
||||||
|
self.structurally_resolve_const(element.span, self.normalize(element.span, count));
|
||||||
|
|
||||||
|
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
|
||||||
|
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
|
||||||
|
if count.references_error() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the length is 0, we don't create any elements, so we don't copy any.
|
||||||
|
// If the length is 1, we don't copy that one element, we move it. Only check
|
||||||
|
// for `Copy` if the length is larger.
|
||||||
|
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
|
||||||
|
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(in super::super) fn check_method_argument_types(
|
pub(in super::super) fn check_method_argument_types(
|
||||||
&self,
|
&self,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
|
|
|
@ -199,6 +199,15 @@ fn typeck_with_inspect<'tcx>(
|
||||||
fcx.write_ty(id, expected_type);
|
fcx.write_ty(id, expected_type);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Whether to check repeat exprs before/after inference fallback is somewhat arbitrary of a decision
|
||||||
|
// as neither option is strictly more permissive than the other. However, we opt to check repeat exprs
|
||||||
|
// first as errors from not having inferred array lengths yet seem less confusing than errors from inference
|
||||||
|
// fallback arbitrarily inferring something incompatible with `Copy` inference side effects.
|
||||||
|
//
|
||||||
|
// This should also be forwards compatible with moving repeat expr checks to a custom goal kind or using
|
||||||
|
// marker traits in the future.
|
||||||
|
fcx.check_repeat_exprs();
|
||||||
|
|
||||||
fcx.type_inference_fallback();
|
fcx.type_inference_fallback();
|
||||||
|
|
||||||
// Even though coercion casts provide type hints, we check casts after fallback for
|
// Even though coercion casts provide type hints, we check casts after fallback for
|
||||||
|
|
|
@ -62,6 +62,9 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
|
||||||
|
|
||||||
pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>)>>,
|
pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>)>>,
|
||||||
|
|
||||||
|
pub(super) deferred_repeat_expr_checks:
|
||||||
|
RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
|
||||||
|
|
||||||
/// Whenever we introduce an adjustment from `!` into a type variable,
|
/// Whenever we introduce an adjustment from `!` into a type variable,
|
||||||
/// we record that type variable here. This is later used to inform
|
/// we record that type variable here. This is later used to inform
|
||||||
/// fallback. See the `fallback` module for details.
|
/// fallback. See the `fallback` module for details.
|
||||||
|
@ -96,6 +99,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
|
||||||
deferred_transmute_checks: RefCell::new(Vec::new()),
|
deferred_transmute_checks: RefCell::new(Vec::new()),
|
||||||
deferred_asm_checks: RefCell::new(Vec::new()),
|
deferred_asm_checks: RefCell::new(Vec::new()),
|
||||||
deferred_coroutine_interiors: RefCell::new(Vec::new()),
|
deferred_coroutine_interiors: RefCell::new(Vec::new()),
|
||||||
|
deferred_repeat_expr_checks: RefCell::new(Vec::new()),
|
||||||
diverging_type_vars: RefCell::new(Default::default()),
|
diverging_type_vars: RefCell::new(Default::default()),
|
||||||
infer_var_info: RefCell::new(Default::default()),
|
infer_var_info: RefCell::new(Default::default()),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue