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
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 217 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/unify.rs | 32 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/coercion.rs | 18 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/invalid_cast.rs | 2 | ||||
| -rw-r--r-- | crates/ide/src/hover/tests.rs | 1 |
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]); } "#, |