Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit b7a4175

Browse files
committed
Implement type inference for assignee expressions
1 parent 62d6b5a commit b7a4175

File tree

4 files changed

+416
-3
lines changed

4 files changed

+416
-3
lines changed

crates/hir-ty/src/infer.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ trait PatLike: Into<ExprOrPatId> + Copy {
137137
) -> Ty;
138138
}
139139

140+
impl PatLike for ExprId {
141+
type BindingMode = ();
142+
143+
fn infer(this: &mut InferenceContext, id: Self, expected_ty: &Ty, _: Self::BindingMode) -> Ty {
144+
this.infer_assignee_expr(id, expected_ty)
145+
}
146+
}
147+
140148
impl PatLike for PatId {
141149
type BindingMode = BindingMode;
142150

crates/hir-ty/src/infer/expr.rs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,8 @@ impl<'a> InferenceContext<'a> {
593593
}
594594
Expr::BinaryOp { lhs, rhs, op } => match op {
595595
Some(BinaryOp::Assignment { op: None }) => {
596-
let lhs_ty = self.infer_expr(*lhs, &Expectation::none());
597-
self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty));
596+
let rhs_ty = self.infer_expr(*rhs, &Expectation::none());
597+
self.infer_assignee_expr(*lhs, &rhs_ty);
598598
self.result.standard_types.unit.clone()
599599
}
600600
Some(BinaryOp::LogicOp(_)) => {
@@ -817,6 +817,95 @@ impl<'a> InferenceContext<'a> {
817817
}
818818
}
819819

820+
pub(super) fn infer_assignee_expr(&mut self, lhs: ExprId, rhs_ty: &Ty) -> Ty {
821+
let is_rest_expr = |expr| {
822+
matches!(
823+
&self.body[expr],
824+
Expr::Range { lhs: None, rhs: None, range_type: RangeOp::Exclusive },
825+
)
826+
};
827+
828+
let rhs_ty = self.resolve_ty_shallow(rhs_ty);
829+
830+
let ty = match &self.body[lhs] {
831+
Expr::Tuple { exprs } => {
832+
// We don't consider multiple ellipses. This is analogous to
833+
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
834+
let ellipsis = exprs.iter().position(|e| is_rest_expr(*e));
835+
let exprs: Vec<_> = exprs.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
836+
837+
self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
838+
}
839+
Expr::Call { callee, args } => {
840+
// Tuple structs
841+
let path = match &self.body[*callee] {
842+
Expr::Path(path) => Some(path),
843+
_ => None,
844+
};
845+
846+
// We don't consider multiple ellipses. This is analogous to
847+
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
848+
let ellipsis = args.iter().position(|e| is_rest_expr(*e));
849+
let args: Vec<_> = args.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
850+
851+
self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
852+
}
853+
Expr::Array(Array::ElementList(elements)) => {
854+
let elem_ty = match rhs_ty.kind(Interner) {
855+
TyKind::Array(st, _) => st.clone(),
856+
_ => self.err_ty(),
857+
};
858+
859+
// There's no need to handle `..` as it cannot be bound.
860+
let sub_exprs = elements.iter().filter(|e| !is_rest_expr(**e));
861+
862+
for e in sub_exprs {
863+
self.infer_assignee_expr(*e, &elem_ty);
864+
}
865+
866+
match rhs_ty.kind(Interner) {
867+
TyKind::Array(_, _) => rhs_ty.clone(),
868+
// Even when `rhs_ty` is not an array type, this assignee
869+
// expression is infered to be an array (of unknown element
870+
// type and length). This should not be just an error type,
871+
// because we are to compute the unifiability of this type and
872+
// `rhs_ty` in the end of this function to issue type mismatches.
873+
_ => TyKind::Array(self.err_ty(), crate::consteval::usize_const(None))
874+
.intern(Interner),
875+
}
876+
}
877+
Expr::RecordLit { path, fields, .. } => {
878+
let subs = fields.iter().map(|f| (f.name.clone(), f.expr));
879+
880+
self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs.into(), subs)
881+
}
882+
Expr::Underscore => rhs_ty.clone(),
883+
_ => {
884+
// `lhs` is a place expression, a unit struct, or an enum variant.
885+
let lhs_ty = self.infer_expr(lhs, &Expectation::none());
886+
887+
// This is the only branch where this function may coerce any type.
888+
// We are returning early to avoid the unifiability check below.
889+
let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
890+
let ty = match self.coerce(None, &rhs_ty, &lhs_ty) {
891+
Ok(ty) => ty,
892+
Err(_) => self.err_ty(),
893+
};
894+
self.write_expr_ty(lhs, ty.clone());
895+
return ty;
896+
}
897+
};
898+
899+
let ty = self.insert_type_vars_shallow(ty);
900+
if !self.unify(&ty, &rhs_ty) {
901+
self.result
902+
.type_mismatches
903+
.insert(lhs.into(), TypeMismatch { expected: rhs_ty.clone(), actual: ty.clone() });
904+
}
905+
self.write_expr_ty(lhs, ty.clone());
906+
ty
907+
}
908+
820909
fn infer_overloadable_binop(
821910
&mut self,
822911
lhs: ExprId,

crates/hir-ty/src/tests/coercion.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,24 @@ fn f(text: &str) {
312312
);
313313
}
314314

315+
#[test]
316+
fn destructuring_assign_coerce() {
317+
check_no_mismatches(
318+
r"
319+
//- minicore: deref
320+
struct String;
321+
impl core::ops::Deref for String { type Target = str; }
322+
fn g(_text: &str) {}
323+
fn f(text: &str) {
324+
let mut text = text;
325+
let tmp = String;
326+
[text, _] = [&tmp, &tmp];
327+
g(text);
328+
}
329+
",
330+
);
331+
}
332+
315333
#[test]
316334
fn coerce_fn_item_to_fn_ptr() {
317335
check_no_mismatches(

0 commit comments

Comments
 (0)