Rollup merge of #130714 - compiler-errors:try-structurally-resolve-const, r=BoxyUwU
Introduce `structurally_normalize_const`, use it in `rustc_hir_typeck`
Introduces `structurally_normalize_const` to typecking to separate the "eval a const" step from the "try to turn a valtree into a target usize" in HIR typeck, where we may still have infer vars and stuff around.
I also changed `check_expr_repeat` to move a double evaluation of a const into a single one. I'll leave inline comments.
r? ```@BoxyUwU```
I hesitated to really test this on the new solver where it probably matters for unevaluated consts. If you're worried about the side-effects, I'd be happy to craft some more tests 😄
This commit is contained in:
commit
2bca5c4fc1
@ -1668,10 +1668,17 @@ pub enum ArrayLen<'hir> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ArrayLen<'_> {
|
impl ArrayLen<'_> {
|
||||||
pub fn hir_id(&self) -> HirId {
|
pub fn span(self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
ArrayLen::Infer(InferArg { hir_id, .. }) | ArrayLen::Body(ConstArg { hir_id, .. }) => {
|
ArrayLen::Infer(arg) => arg.span,
|
||||||
*hir_id
|
ArrayLen::Body(body) => body.span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hir_id(self) -> HirId {
|
||||||
|
match self {
|
||||||
|
ArrayLen::Infer(InferArg { hir_id, .. }) | ArrayLen::Body(&ConstArg { hir_id, .. }) => {
|
||||||
|
hir_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1491,8 +1491,10 @@ fn check_expr_repeat(
|
|||||||
expr: &'tcx hir::Expr<'tcx>,
|
expr: &'tcx hir::Expr<'tcx>,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let count = self.lower_array_length(count);
|
let count_span = count.span();
|
||||||
if let Some(count) = count.try_eval_target_usize(tcx, self.param_env) {
|
let count = self.try_structurally_resolve_const(count_span, self.lower_array_length(count));
|
||||||
|
|
||||||
|
if let Some(count) = count.try_to_target_usize(tcx) {
|
||||||
self.suggest_array_len(expr, count);
|
self.suggest_array_len(expr, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1520,19 +1522,24 @@ fn check_expr_repeat(
|
|||||||
return Ty::new_error(tcx, guar);
|
return Ty::new_error(tcx, guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_repeat_element_needs_copy_bound(element, count, element_ty);
|
// 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) {
|
||||||
|
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
|
||||||
|
}
|
||||||
|
|
||||||
let ty = Ty::new_array_with_const_len(tcx, t, count);
|
let ty = Ty::new_array_with_const_len(tcx, t, count);
|
||||||
|
|
||||||
self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None));
|
self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None));
|
||||||
|
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_repeat_element_needs_copy_bound(
|
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
|
||||||
|
fn enforce_repeat_element_needs_copy_bound(
|
||||||
&self,
|
&self,
|
||||||
element: &hir::Expr<'_>,
|
element: &hir::Expr<'_>,
|
||||||
count: ty::Const<'tcx>,
|
|
||||||
element_ty: Ty<'tcx>,
|
element_ty: Ty<'tcx>,
|
||||||
) {
|
) {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
@ -1565,27 +1572,23 @@ fn check_repeat_element_needs_copy_bound(
|
|||||||
_ => traits::IsConstable::No,
|
_ => traits::IsConstable::No,
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
|
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
|
||||||
// don't copy that one element, we move it. Only check for Copy if the length is larger.
|
let code = traits::ObligationCauseCode::RepeatElementCopy {
|
||||||
if count.try_eval_target_usize(tcx, self.param_env).is_none_or(|len| len > 1) {
|
is_constable,
|
||||||
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
|
elt_type: element_ty,
|
||||||
let code = traits::ObligationCauseCode::RepeatElementCopy {
|
elt_span: element.span,
|
||||||
is_constable,
|
elt_stmt_span: self
|
||||||
elt_type: element_ty,
|
.tcx
|
||||||
elt_span: element.span,
|
.hir()
|
||||||
elt_stmt_span: self
|
.parent_iter(element.hir_id)
|
||||||
.tcx
|
.find_map(|(_, node)| match node {
|
||||||
.hir()
|
hir::Node::Item(it) => Some(it.span),
|
||||||
.parent_iter(element.hir_id)
|
hir::Node::Stmt(stmt) => Some(stmt.span),
|
||||||
.find_map(|(_, node)| match node {
|
_ => None,
|
||||||
hir::Node::Item(it) => Some(it.span),
|
})
|
||||||
hir::Node::Stmt(stmt) => Some(stmt.span),
|
.expect("array repeat expressions must be inside an item or statement"),
|
||||||
_ => None,
|
};
|
||||||
})
|
self.require_type_meets(element_ty, element.span, code, lang_item);
|
||||||
.expect("array repeat expressions must be inside an item or statement"),
|
|
||||||
};
|
|
||||||
self.require_type_meets(element_ty, element.span, code, lang_item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr_tuple(
|
fn check_expr_tuple(
|
||||||
@ -2800,9 +2803,10 @@ fn maybe_suggest_array_indexing(
|
|||||||
len: ty::Const<'tcx>,
|
len: ty::Const<'tcx>,
|
||||||
) {
|
) {
|
||||||
err.span_label(field.span, "unknown field");
|
err.span_label(field.span, "unknown field");
|
||||||
if let (Some(len), Ok(user_index)) =
|
if let (Some(len), Ok(user_index)) = (
|
||||||
(len.try_eval_target_usize(self.tcx, self.param_env), field.as_str().parse::<u64>())
|
self.try_structurally_resolve_const(base.span, len).try_to_target_usize(self.tcx),
|
||||||
{
|
field.as_str().parse::<u64>(),
|
||||||
|
) {
|
||||||
let help = "instead of using tuple indexing, use array indexing";
|
let help = "instead of using tuple indexing, use array indexing";
|
||||||
let applicability = if len < user_index {
|
let applicability = if len < user_index {
|
||||||
Applicability::MachineApplicable
|
Applicability::MachineApplicable
|
||||||
|
@ -1470,6 +1470,33 @@ pub fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self, sp), ret)]
|
||||||
|
pub fn try_structurally_resolve_const(&self, sp: Span, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
|
// FIXME(min_const_generic_exprs): We could process obligations here if `ct` is a var.
|
||||||
|
|
||||||
|
if self.next_trait_solver()
|
||||||
|
&& let ty::ConstKind::Unevaluated(..) = ct.kind()
|
||||||
|
{
|
||||||
|
// We need to use a separate variable here as otherwise the temporary for
|
||||||
|
// `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting
|
||||||
|
// in a reentrant borrow, causing an ICE.
|
||||||
|
let result = self
|
||||||
|
.at(&self.misc(sp), self.param_env)
|
||||||
|
.structurally_normalize_const(ct, &mut **self.fulfillment_cx.borrow_mut());
|
||||||
|
match result {
|
||||||
|
Ok(normalized_ct) => normalized_ct,
|
||||||
|
Err(errors) => {
|
||||||
|
let guar = self.err_ctxt().report_fulfillment_errors(errors);
|
||||||
|
return ty::Const::new_error(self.tcx, guar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if self.tcx.features().generic_const_exprs {
|
||||||
|
ct.normalize(self.tcx, self.param_env)
|
||||||
|
} else {
|
||||||
|
ct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolves `ty` by a single level if `ty` is a type variable.
|
/// Resolves `ty` by a single level if `ty` is a type variable.
|
||||||
///
|
///
|
||||||
/// When the new solver is enabled, this will also attempt to normalize
|
/// When the new solver is enabled, this will also attempt to normalize
|
||||||
|
@ -1502,7 +1502,10 @@ pub(crate) fn suggest_deref_unwrap_or(
|
|||||||
// Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
|
// Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
|
||||||
let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
|
let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
|
||||||
&& let ty::Infer(_) = elem_ty.kind()
|
&& let ty::Infer(_) = elem_ty.kind()
|
||||||
&& size.try_eval_target_usize(self.tcx, self.param_env) == Some(0)
|
&& self
|
||||||
|
.try_structurally_resolve_const(provided_expr.span, *size)
|
||||||
|
.try_to_target_usize(self.tcx)
|
||||||
|
== Some(0)
|
||||||
{
|
{
|
||||||
let slice = Ty::new_slice(self.tcx, *elem_ty);
|
let slice = Ty::new_slice(self.tcx, *elem_ty);
|
||||||
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
|
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
|
||||||
|
@ -101,7 +101,9 @@ pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(SizeSkeleton::Generic(size)) => {
|
Ok(SizeSkeleton::Generic(size)) => {
|
||||||
if let Some(size) = size.try_eval_target_usize(tcx, self.param_env) {
|
if let Some(size) =
|
||||||
|
self.try_structurally_resolve_const(span, size).try_to_target_usize(tcx)
|
||||||
|
{
|
||||||
format!("{size} bytes")
|
format!("{size} bytes")
|
||||||
} else {
|
} else {
|
||||||
format!("generic size {size}")
|
format!("generic size {size}")
|
||||||
|
@ -2412,7 +2412,7 @@ fn check_array_pat_len(
|
|||||||
len: ty::Const<'tcx>,
|
len: ty::Const<'tcx>,
|
||||||
min_len: u64,
|
min_len: u64,
|
||||||
) -> (Option<Ty<'tcx>>, Ty<'tcx>) {
|
) -> (Option<Ty<'tcx>>, Ty<'tcx>) {
|
||||||
let len = len.try_eval_target_usize(self.tcx, self.param_env);
|
let len = self.try_structurally_resolve_const(span, len).try_to_target_usize(self.tcx);
|
||||||
|
|
||||||
let guar = if let Some(len) = len {
|
let guar = if let Some(len) = len {
|
||||||
// Now we know the length...
|
// Now we know the length...
|
||||||
|
@ -46,4 +46,44 @@ fn structurally_normalize<E: 'tcx>(
|
|||||||
Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx))
|
Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn structurally_normalize_const<E: 'tcx>(
|
||||||
|
&self,
|
||||||
|
ct: ty::Const<'tcx>,
|
||||||
|
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
|
||||||
|
) -> Result<ty::Const<'tcx>, Vec<E>> {
|
||||||
|
assert!(!ct.is_ct_infer(), "should have resolved vars before calling");
|
||||||
|
|
||||||
|
if self.infcx.next_trait_solver() {
|
||||||
|
let ty::ConstKind::Unevaluated(..) = ct.kind() else {
|
||||||
|
return Ok(ct);
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_infer_ct = self.infcx.next_const_var(self.cause.span);
|
||||||
|
|
||||||
|
// We simply emit an `alias-eq` goal here, since that will take care of
|
||||||
|
// normalizing the LHS of the projection until it is a rigid projection
|
||||||
|
// (or a not-yet-defined opaque in scope).
|
||||||
|
let obligation = Obligation::new(
|
||||||
|
self.infcx.tcx,
|
||||||
|
self.cause.clone(),
|
||||||
|
self.param_env,
|
||||||
|
ty::PredicateKind::AliasRelate(
|
||||||
|
ct.into(),
|
||||||
|
new_infer_ct.into(),
|
||||||
|
ty::AliasRelationDirection::Equate,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
fulfill_cx.register_predicate_obligation(self.infcx, obligation);
|
||||||
|
let errors = fulfill_cx.select_where_possible(self.infcx);
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.infcx.resolve_vars_if_possible(new_infer_ct))
|
||||||
|
} else {
|
||||||
|
Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ error[E0308]: mismatched types
|
|||||||
--> $DIR/different-fn.rs:10:5
|
--> $DIR/different-fn.rs:10:5
|
||||||
|
|
|
|
||||||
LL | [0; size_of::<Foo<T>>()]
|
LL | [0; size_of::<Foo<T>>()]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected `size_of::<T>()`, found `size_of::<Foo<T>>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected `size_of::<T>()`, found `0`
|
||||||
|
|
|
|
||||||
= note: expected constant `size_of::<T>()`
|
= note: expected constant `size_of::<T>()`
|
||||||
found constant `size_of::<Foo<T>>()`
|
found constant `0`
|
||||||
|
|
||||||
error: unconstrained generic constant
|
error: unconstrained generic constant
|
||||||
--> $DIR/different-fn.rs:10:9
|
--> $DIR/different-fn.rs:10:9
|
||||||
|
Loading…
Reference in New Issue
Block a user