Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22271 from ChayimFriedman2/port-array
fix: Port array and ref exprs inference from rustc
Lukas Wirth 13 days ago
parent c2b0c4b · parent e267ff1 · commit 7202651
-rw-r--r--crates/hir-ty/src/infer/expr.rs217
-rw-r--r--crates/hir-ty/src/infer/unify.rs32
-rw-r--r--crates/hir-ty/src/tests/coercion.rs18
-rw-r--r--crates/ide-diagnostics/src/handlers/invalid_cast.rs2
-rw-r--r--crates/ide/src/hover/tests.rs1
5 files changed, 185 insertions, 85 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index e07a1b2ba4..b1e983f274 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -610,34 +610,13 @@ impl<'db> InferenceContext<'_, 'db> {
self.deferred_cast_checks.push(CastCheck::new(tgt_expr, *expr, expr_ty, cast_ty));
cast_ty
}
- Expr::Ref { expr, rawness, mutability } => {
- let mutability = lower_mutability(*mutability);
- let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected
- .only_has_type(&mut self.table)
- .as_ref()
- .and_then(|t| t.as_reference_or_ptr())
- {
- if exp_mutability == Mutability::Mut && mutability == Mutability::Not {
- // FIXME: record type error - expected mut reference but found shared ref,
- // which cannot be coerced
- }
- if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
- // FIXME: record type error - expected reference but found ptr,
- // which cannot be coerced
- }
- Expectation::rvalue_hint(self, exp_inner)
- } else {
- Expectation::none()
- };
- let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes);
- match rawness {
- Rawness::RawPtr => Ty::new_ptr(self.interner(), inner_ty, mutability),
- Rawness::Ref => {
- let lt = self.table.next_region_var(tgt_expr.into());
- Ty::new_ref(self.interner(), lt, inner_ty, mutability)
- }
- }
- }
+ Expr::Ref { expr, rawness, mutability } => self.infer_ref_expr(
+ *rawness,
+ lower_mutability(*mutability),
+ *expr,
+ expected,
+ tgt_expr,
+ ),
&Expr::Box { expr } => self.infer_expr_box(expr, expected),
Expr::UnaryOp { expr, op } => self.infer_unop_expr(*op, *expr, expected, tgt_expr),
Expr::BinaryOp { lhs, rhs, op } => match op {
@@ -772,7 +751,12 @@ impl<'db> InferenceContext<'_, 'db> {
Ty::new_tup(self.interner(), &tys)
}
- Expr::Array(array) => self.infer_expr_array(tgt_expr, array, expected),
+ Expr::Array(Array::ElementList { elements }) => {
+ self.infer_array_elements_expr(elements, expected, tgt_expr)
+ }
+ Expr::Array(Array::Repeat { initializer, repeat }) => {
+ self.infer_array_repeat_expr(*initializer, *repeat, expected, tgt_expr)
+ }
Expr::Literal(lit) => match lit {
Literal::Bool(..) => self.types.types.bool,
Literal::String(..) => self.types.types.static_str_ref,
@@ -968,6 +952,54 @@ impl<'db> InferenceContext<'_, 'db> {
ty
}
+ fn infer_ref_expr(
+ &mut self,
+ rawness: Rawness,
+ mutbl: Mutability,
+ oprnd: ExprId,
+ expected: &Expectation<'db>,
+ expr: ExprId,
+ ) -> Ty<'db> {
+ let hint = expected.only_has_type(&mut self.table).map_or(Expectation::None, |ty| {
+ match self.table.resolve_vars_with_obligations(ty).kind() {
+ TyKind::Ref(_, ty, _) | TyKind::RawPtr(ty, _) => {
+ if self.is_syntactic_place_expr(oprnd) {
+ // Places may legitimately have unsized types.
+ // For example, dereferences of a wide pointer and
+ // the last field of a struct can be unsized.
+ Expectation::has_type(ty)
+ } else {
+ Expectation::rvalue_hint(self, ty)
+ }
+ }
+ _ => Expectation::None,
+ }
+ });
+ let ty = self.infer_expr_inner(oprnd, &hint, ExprIsRead::No);
+
+ match rawness {
+ Rawness::RawPtr => Ty::new_ptr(self.interner(), ty, mutbl),
+ Rawness::Ref => {
+ // Note: at this point, we cannot say what the best lifetime
+ // is to use for resulting pointer. We want to use the
+ // shortest lifetime possible so as to avoid spurious borrowck
+ // errors. Moreover, the longest lifetime will depend on the
+ // precise details of the value whose address is being taken
+ // (and how long it is valid), which we don't know yet until
+ // type inference is complete.
+ //
+ // Therefore, here we simply generate a region variable. The
+ // region inferencer will then select a suitable value.
+ // Finally, borrowck will infer the value of the region again,
+ // this time with enough precision to check that the value
+ // whose address was taken can actually be made to live as long
+ // as it needs to live.
+ let region = self.table.next_region_var(expr.into());
+ Ty::new_ref(self.interner(), region, ty, mutbl)
+ }
+ }
+ }
+
fn infer_await_expr(&mut self, expr: ExprId, awaitee: ExprId) -> Ty<'db> {
let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes);
let (Some(into_future), Some(into_future_output)) =
@@ -1336,68 +1368,93 @@ impl<'db> InferenceContext<'_, 'db> {
oprnd_t
}
- fn infer_expr_array(
+ fn infer_array_repeat_expr(
&mut self,
- expr: ExprId,
- array: &Array,
+ element: ExprId,
+ count: ExprId,
expected: &Expectation<'db>,
+ expr: ExprId,
) -> Ty<'db> {
- let elem_ty = match expected
- .to_option(&mut self.table)
- .map(|t| self.table.try_structurally_resolve_type(expr.into(), t).kind())
- {
- Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st,
- _ => self.table.next_ty_var(expr.into()),
+ let interner = self.interner();
+ let usize = self.types.types.usize;
+ let count_ct = match self.store[count] {
+ Expr::Underscore => {
+ self.write_expr_ty(count, usize);
+ self.table.next_const_var(count.into())
+ }
+ _ => {
+ self.infer_expr(count, &Expectation::HasType(usize), ExprIsRead::Yes);
+ consteval::eval_to_const(count, self)
+ }
};
+ let count = self.table.try_structurally_resolve_const(count.into(), count_ct);
+ let count = self.insert_const_vars_shallow(count);
- let krate = self.resolver.krate();
+ let uty = match expected {
+ Expectation::HasType(uty) => uty.builtin_index(),
+ _ => None,
+ };
- let expected = Expectation::has_type(elem_ty);
- let (elem_ty, len) = match array {
- Array::ElementList { elements, .. } if elements.is_empty() => {
- (elem_ty, consteval::usize_const(self.db, Some(0), krate))
+ let t = match uty {
+ Some(uty) => {
+ self.infer_expr_coerce(element, &Expectation::has_type(uty), ExprIsRead::Yes);
+ uty
}
- Array::ElementList { elements, .. } => {
- let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements);
- for &expr in elements.iter() {
- let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes);
- coerce.coerce(
- self,
- &ObligationCause::new(expr),
- expr,
- cur_elem_ty,
- ExprIsRead::Yes,
- );
- }
- (
- coerce.complete(self),
- consteval::usize_const(self.db, Some(elements.len() as u128), krate),
- )
+ None => {
+ let ty = self.table.next_ty_var(element.into());
+ self.infer_expr(element, &Expectation::has_type(ty), ExprIsRead::Yes);
+ ty
}
- &Array::Repeat { initializer, repeat } => {
- self.infer_expr_coerce(
- initializer,
- &Expectation::has_type(elem_ty),
- ExprIsRead::Yes,
- );
- let usize = self.types.types.usize;
- let len = match self.store[repeat] {
- Expr::Underscore => {
- self.write_expr_ty(repeat, usize);
- self.table.next_const_var(repeat.into())
- }
- _ => {
- self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes);
- consteval::eval_to_const(repeat, self)
- }
- };
+ };
+
+ // 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.
+ // FIXME: Do it here like rustc.
+ // self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
+
+ let ty = Ty::new_array_with_const_len(interner, t, count);
+ self.table.register_wf_obligation(ty.into(), ObligationCause::new(expr));
+ ty
+ }
- (elem_ty, len)
+ fn infer_array_elements_expr(
+ &mut self,
+ args: &[ExprId],
+ expected: &Expectation<'db>,
+ expr: ExprId,
+ ) -> Ty<'db> {
+ let element_ty = if !args.is_empty() {
+ let coerce_to = expected
+ .to_option(&mut self.table)
+ .and_then(|uty| {
+ self.table
+ .resolve_vars_with_obligations(uty)
+ .builtin_index()
+ // Avoid using the original type variable as the coerce_to type, as it may resolve
+ // during the first coercion instead of being the LUB type.
+ .filter(|t| !self.table.resolve_vars_with_obligations(*t).is_ty_var())
+ })
+ .unwrap_or_else(|| self.table.next_ty_var(expr.into()));
+ let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
+
+ for &e in args {
+ // FIXME: the element expectation should use
+ // `try_structurally_resolve_and_adjust_for_branches` just like in `if` and `match`.
+ // While that fixes nested coercion, it will break [some
+ // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528).
+ // If we find a way to support recursive tuple coercion, this break can be avoided.
+ let e_ty =
+ self.infer_expr_inner(e, &Expectation::has_type(coerce_to), ExprIsRead::Yes);
+ let cause = ObligationCause::new(e);
+ coerce.coerce(self, &cause, e, e_ty, ExprIsRead::Yes);
}
+ coerce.complete(self)
+ } else {
+ self.table.next_ty_var(expr.into())
};
- // Try to evaluate unevaluated constant, and insert variable if is not possible.
- let len = self.insert_const_vars_shallow(len);
- Ty::new_array_with_const_len(self.interner(), elem_ty, len)
+ let array_len = args.len() as u128;
+ Ty::new_array(self.interner(), element_ty, array_len)
}
pub(super) fn infer_return(&mut self, expr: ExprId) {
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 6a34c5b8e5..bb6e383e7d 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -7,7 +7,7 @@ use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId};
use rustc_hash::FxHashSet;
use rustc_type_ir::{
TyVid, TypeFoldable, TypeVisitableExt,
- inherent::{GenericArg as _, IntoKind, Ty as _},
+ inherent::{Const as _, GenericArg as _, IntoKind, Ty as _},
solve::Certainty,
};
use smallvec::SmallVec;
@@ -17,9 +17,9 @@ use crate::{
InferenceDiagnostic, Span,
db::HirDatabase,
next_solver::{
- Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
- ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind,
- TypingMode,
+ Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg,
+ GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty,
+ TyKind, TypingMode,
fulfill::{FulfillmentCtxt, NextSolverError},
infer::{
DbInternerInferExt, InferCtxt, InferOk,
@@ -342,6 +342,30 @@ impl<'db> InferenceTable<'db> {
}
}
+ pub(crate) fn try_structurally_resolve_const(
+ &mut self,
+ sp: Span,
+ ct: Const<'db>,
+ ) -> Const<'db> {
+ let ct = self.resolve_vars_with_obligations(ct);
+
+ if let ConstKind::Unevaluated(..) = ct.kind() {
+ let result = self
+ .infer_ctxt
+ .at(&ObligationCause::new(sp), self.param_env)
+ .structurally_normalize_const(ct, &mut self.fulfillment_cx);
+ match result {
+ Ok(normalized_ct) => normalized_ct,
+ Err(errors) => {
+ self.trait_errors.extend(errors);
+ Const::new_error(self.interner(), ErrorGuaranteed)
+ }
+ }
+ } else {
+ ct
+ }
+ }
+
pub(crate) fn snapshot(&mut self) -> CombinedSnapshot {
self.infer_ctxt.start_snapshot()
}
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 05dbb1a8ac..a7f65d4fe8 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -1036,3 +1036,21 @@ fn f() {
"#,
);
}
+
+#[test]
+fn regression_22270() {
+ check_no_mismatches(
+ r#"
+fn a() {}
+fn b() {}
+
+fn foo<T, const N: usize>(x: [T; N]) -> Vec<T> {
+ loop {}
+}
+
+fn bar() {
+ foo([a, b]);
+}
+ "#,
+ );
+}
diff --git a/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index c004ee31ae..bd8fa69e28 100644
--- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -990,7 +990,7 @@ fn main() {
fn rustc_issue_106883() {
check_diagnostics_with_disabled(
r#"
-//- minicore: sized, deref
+//- minicore: sized, deref, coerce_unsized, unsize
use core::ops::Deref;
struct Foo;
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 89d467cef8..6d9c6d4c21 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -7152,6 +7152,7 @@ fn f() { let expr = [1, 2, $03$0, 4] }
fn hover_range_functions() {
check_hover_range(
r#"
+//- minicore: unsize, coerce_unsized
fn f<T>(a: &[T]) { }
fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
"#,