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.rs | 99 |
1 files changed, 97 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 8be236421f..a1a7b17f37 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -593,8 +593,8 @@ impl<'a> InferenceContext<'a> { } Expr::BinaryOp { lhs, rhs, op } => match op { Some(BinaryOp::Assignment { op: None }) => { - let lhs_ty = self.infer_expr(*lhs, &Expectation::none()); - self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty)); + let rhs_ty = self.infer_expr(*rhs, &Expectation::none()); + self.infer_assignee_expr(*lhs, &rhs_ty); self.result.standard_types.unit.clone() } Some(BinaryOp::LogicOp(_)) => { @@ -775,6 +775,12 @@ impl<'a> InferenceContext<'a> { }, }, Expr::MacroStmts { tail } => self.infer_expr_inner(*tail, expected), + Expr::Underscore => { + // Underscore expressions may only appear in assignee expressions, + // which are handled by `infer_assignee_expr()`, so any underscore + // expression reaching this branch is an error. + self.err_ty() + } }; // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); @@ -811,6 +817,95 @@ impl<'a> InferenceContext<'a> { } } + pub(super) fn infer_assignee_expr(&mut self, lhs: ExprId, rhs_ty: &Ty) -> Ty { + let is_rest_expr = |expr| { + matches!( + &self.body[expr], + Expr::Range { lhs: None, rhs: None, range_type: RangeOp::Exclusive }, + ) + }; + + let rhs_ty = self.resolve_ty_shallow(rhs_ty); + + let ty = match &self.body[lhs] { + Expr::Tuple { exprs } => { + // We don't consider multiple ellipses. This is analogous to + // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`. + let ellipsis = exprs.iter().position(|e| is_rest_expr(*e)); + let exprs: Vec<_> = exprs.iter().filter(|e| !is_rest_expr(**e)).copied().collect(); + + self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs) + } + Expr::Call { callee, args } => { + // Tuple structs + let path = match &self.body[*callee] { + Expr::Path(path) => Some(path), + _ => None, + }; + + // We don't consider multiple ellipses. This is analogous to + // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`. + let ellipsis = args.iter().position(|e| is_rest_expr(*e)); + let args: Vec<_> = args.iter().filter(|e| !is_rest_expr(**e)).copied().collect(); + + self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args) + } + Expr::Array(Array::ElementList(elements)) => { + let elem_ty = match rhs_ty.kind(Interner) { + TyKind::Array(st, _) => st.clone(), + _ => self.err_ty(), + }; + + // There's no need to handle `..` as it cannot be bound. + let sub_exprs = elements.iter().filter(|e| !is_rest_expr(**e)); + + for e in sub_exprs { + self.infer_assignee_expr(*e, &elem_ty); + } + + match rhs_ty.kind(Interner) { + TyKind::Array(_, _) => rhs_ty.clone(), + // Even when `rhs_ty` is not an array type, this assignee + // expression is infered to be an array (of unknown element + // type and length). This should not be just an error type, + // because we are to compute the unifiability of this type and + // `rhs_ty` in the end of this function to issue type mismatches. + _ => TyKind::Array(self.err_ty(), crate::consteval::usize_const(None)) + .intern(Interner), + } + } + Expr::RecordLit { path, fields, .. } => { + let subs = fields.iter().map(|f| (f.name.clone(), f.expr)); + + self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs.into(), subs) + } + Expr::Underscore => rhs_ty.clone(), + _ => { + // `lhs` is a place expression, a unit struct, or an enum variant. + let lhs_ty = self.infer_expr(lhs, &Expectation::none()); + + // This is the only branch where this function may coerce any type. + // We are returning early to avoid the unifiability check below. + let lhs_ty = self.insert_type_vars_shallow(lhs_ty); + let ty = match self.coerce(None, &rhs_ty, &lhs_ty) { + Ok(ty) => ty, + Err(_) => self.err_ty(), + }; + self.write_expr_ty(lhs, ty.clone()); + return ty; + } + }; + + let ty = self.insert_type_vars_shallow(ty); + if !self.unify(&ty, &rhs_ty) { + self.result + .type_mismatches + .insert(lhs.into(), TypeMismatch { expected: rhs_ty.clone(), actual: ty.clone() }); + } + self.write_expr_ty(lhs, ty.clone()); + ty + } + fn infer_overloadable_binop( &mut self, lhs: ExprId, |