mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Defer repeat expr Copy
check
This commit is contained in:
parent
cdd8895d72
commit
dc6db192c4
@ -1853,12 +1853,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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 1, we don't copy that one element, we move it. Only check
|
||||
// for `Copy` if the length is larger, or unevaluated.
|
||||
// FIXME(min_const_generic_exprs): We could perhaps defer this check so that
|
||||
// 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) {
|
||||
} else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
|
||||
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).
|
||||
fn enforce_repeat_element_needs_copy_bound(
|
||||
pub(super) fn enforce_repeat_element_needs_copy_bound(
|
||||
&self,
|
||||
element: &hir::Expr<'_>,
|
||||
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
|
||||
/// also select obligations if it seems useful, in an effort
|
||||
/// to get more type information.
|
||||
// FIXME(-Znext-solver): A lot of the calls to this method should
|
||||
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
|
||||
#[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.
|
||||
if !ty.has_non_region_infer() {
|
||||
if !t.has_non_region_infer() {
|
||||
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.
|
||||
ty = self.resolve_vars_if_possible(ty);
|
||||
if !ty.has_non_region_infer() {
|
||||
debug!(?ty);
|
||||
return ty;
|
||||
// If `t` is a type variable, see whether we already know what it is.
|
||||
t = self.resolve_vars_if_possible(t);
|
||||
if !t.has_non_region_infer() {
|
||||
debug!(?t);
|
||||
return t;
|
||||
}
|
||||
|
||||
// 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
|
||||
// precisely.
|
||||
self.select_obligations_where_possible(|_| {});
|
||||
self.resolve_vars_if_possible(ty)
|
||||
self.resolve_vars_if_possible(t)
|
||||
}
|
||||
|
||||
pub(crate) fn record_deferred_call_resolution(
|
||||
@ -1454,7 +1457,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
sp: Span,
|
||||
ct: 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()
|
||||
&& 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>(
|
||||
&self,
|
||||
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(
|
||||
&self,
|
||||
sp: Span,
|
||||
|
@ -199,6 +199,15 @@ fn typeck_with_inspect<'tcx>(
|
||||
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();
|
||||
|
||||
// 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_repeat_expr_checks:
|
||||
RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
|
||||
|
||||
/// Whenever we introduce an adjustment from `!` into a type variable,
|
||||
/// we record that type variable here. This is later used to inform
|
||||
/// fallback. See the `fallback` module for details.
|
||||
@ -96,6 +99,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
|
||||
deferred_transmute_checks: RefCell::new(Vec::new()),
|
||||
deferred_asm_checks: 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()),
|
||||
infer_var_info: RefCell::new(Default::default()),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user