Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/expr.rs')
-rw-r--r--crates/hir-ty/src/infer/expr.rs217
1 files changed, 137 insertions, 80 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) {