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.rs245
1 files changed, 69 insertions, 176 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 657e4d7796..32b4ea2f28 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -9,8 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
use either::Either;
use hir_def::{
hir::{
- ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
- Literal, Pat, PatId, Statement, UnaryOp,
+ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId,
+ LabelId, Literal, Pat, PatId, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs, Path},
@@ -188,6 +188,9 @@ impl InferenceContext<'_> {
| Pat::ConstBlock(_)
| Pat::Record { .. }
| Pat::Missing => true,
+ Pat::Expr(_) => unreachable!(
+ "we don't call pat_guaranteed_to_constitute_read_for_never() with assignments"
+ ),
}
}
@@ -195,10 +198,14 @@ impl InferenceContext<'_> {
match &self.body[expr] {
// Lang item paths cannot currently be local variables or statics.
Expr::Path(Path::LangItem(_, _)) => false,
- Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
+ Expr::Path(Path::Normal(path)) => path.type_anchor().is_none(),
Expr::Path(path) => self
.resolver
- .resolve_path_in_value_ns_fully(self.db.upcast(), path)
+ .resolve_path_in_value_ns_fully(
+ self.db.upcast(),
+ path,
+ self.body.expr_path_hygiene(expr),
+ )
.map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))),
Expr::Underscore => true,
Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
@@ -223,6 +230,7 @@ impl InferenceContext<'_> {
| Expr::Const(..)
| Expr::UnaryOp { .. }
| Expr::BinaryOp { .. }
+ | Expr::Assignment { .. }
| Expr::Yield { .. }
| Expr::Cast { .. }
| Expr::Async { .. }
@@ -374,7 +382,7 @@ impl InferenceContext<'_> {
// collect explicitly written argument types
for arg_type in arg_types.iter() {
let arg_ty = match arg_type {
- Some(type_ref) => self.make_ty(type_ref),
+ Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
sig_tys.push(arg_ty);
@@ -382,7 +390,7 @@ impl InferenceContext<'_> {
// add return type
let ret_ty = match ret_type {
- Some(type_ref) => self.make_ty(type_ref),
+ Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
if let ClosureKind::Async = closure_kind {
@@ -609,23 +617,7 @@ impl InferenceContext<'_> {
coerce.complete(self)
}
}
- Expr::Path(p) => {
- let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
- let ty = match self.infer_path(p, tgt_expr.into()) {
- Some(ty) => ty,
- None => {
- if matches!(p, Path::Normal { mod_path, .. } if mod_path.is_ident() || mod_path.is_self())
- {
- self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent {
- expr: tgt_expr,
- });
- }
- self.err_ty()
- }
- };
- self.resolver.reset_to_guard(g);
- ty
- }
+ Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr),
&Expr::Continue { label } => {
if find_continuable(&mut self.breakables, label).is_none() {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
@@ -794,7 +786,7 @@ impl InferenceContext<'_> {
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
}
Expr::Cast { expr, type_ref } => {
- let cast_ty = self.make_ty(type_ref);
+ let cast_ty = self.make_body_ty(*type_ref);
let expr_ty = self.infer_expr(
*expr,
&Expectation::Castable(cast_ty.clone()),
@@ -892,36 +884,6 @@ impl InferenceContext<'_> {
}
}
Expr::BinaryOp { lhs, rhs, op } => match op {
- Some(BinaryOp::Assignment { op: None }) => {
- let lhs = *lhs;
- let is_ordinary = match &self.body[lhs] {
- Expr::Array(_)
- | Expr::RecordLit { .. }
- | Expr::Tuple { .. }
- | Expr::Underscore => false,
- Expr::Call { callee, .. } => !matches!(&self.body[*callee], Expr::Path(_)),
- _ => true,
- };
-
- // In ordinary (non-destructuring) assignments, the type of
- // `lhs` must be inferred first so that the ADT fields
- // instantiations in RHS can be coerced to it. Note that this
- // cannot happen in destructuring assignments because of how
- // they are desugared.
- if is_ordinary {
- // LHS of assignment doesn't constitute reads.
- let lhs_ty = self.infer_expr(lhs, &Expectation::none(), ExprIsRead::No);
- self.infer_expr_coerce(
- *rhs,
- &Expectation::has_type(lhs_ty),
- ExprIsRead::No,
- );
- } else {
- let rhs_ty = self.infer_expr(*rhs, &Expectation::none(), ExprIsRead::Yes);
- self.infer_assignee_expr(lhs, &rhs_ty);
- }
- self.result.standard_types.unit.clone()
- }
Some(BinaryOp::LogicOp(_)) => {
let bool_ty = self.result.standard_types.bool_.clone();
self.infer_expr_coerce(
@@ -942,6 +904,35 @@ impl InferenceContext<'_> {
Some(op) => self.infer_overloadable_binop(*lhs, *op, *rhs, tgt_expr),
_ => self.err_ty(),
},
+ &Expr::Assignment { target, value } => {
+ // In ordinary (non-destructuring) assignments, the type of
+ // `lhs` must be inferred first so that the ADT fields
+ // instantiations in RHS can be coerced to it. Note that this
+ // cannot happen in destructuring assignments because of how
+ // they are desugared.
+ let lhs_ty = match &self.body[target] {
+ // LHS of assignment doesn't constitute reads.
+ &Pat::Expr(expr) => {
+ Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No))
+ }
+ Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)),
+ _ => None,
+ };
+
+ if let Some(lhs_ty) = lhs_ty {
+ self.write_pat_ty(target, lhs_ty.clone());
+ self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No);
+ } else {
+ let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes);
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
+ self.inside_assignment = true;
+ self.infer_top_pat(target, &rhs_ty);
+ self.inside_assignment = false;
+ self.resolver.reset_to_guard(resolver_guard);
+ }
+ self.result.standard_types.unit.clone()
+ }
Expr::Range { lhs, rhs, range_type } => {
let lhs_ty =
lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes));
@@ -981,7 +972,7 @@ impl InferenceContext<'_> {
(RangeOp::Inclusive, _, None) => self.err_ty(),
}
}
- Expr::Index { base, index, is_assignee_expr } => {
+ Expr::Index { base, index } => {
let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes);
let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
@@ -1017,23 +1008,11 @@ impl InferenceContext<'_> {
self.write_method_resolution(tgt_expr, func, subst);
}
let assoc = self.resolve_ops_index_output();
- let res = self.resolve_associated_type_with_params(
+ self.resolve_associated_type_with_params(
self_ty.clone(),
assoc,
&[index_ty.clone().cast(Interner)],
- );
-
- if *is_assignee_expr {
- if let Some(index_trait) = self.resolve_lang_trait(LangItem::IndexMut) {
- let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
- .push(self_ty)
- .fill(|_| index_ty.clone().cast(Interner))
- .build();
- self.push_obligation(trait_ref.cast(Interner));
- }
- }
-
- res
+ )
} else {
self.err_ty()
}
@@ -1151,9 +1130,7 @@ impl InferenceContext<'_> {
},
},
Expr::Underscore => {
- // Underscore expressions may only appear in assignee expressions,
- // which are handled by `infer_assignee_expr()`.
- // Any other underscore expression is an error, we render a specialized diagnostic
+ // Underscore expression is an error, we render a specialized diagnostic
// to let the user know what type is expected though.
let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty());
self.push_diagnostic(InferenceDiagnostic::TypedHole {
@@ -1232,6 +1209,22 @@ impl InferenceContext<'_> {
ty
}
+ fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty {
+ let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, scope_id);
+ let ty = match self.infer_path(path, id) {
+ Some(ty) => ty,
+ None => {
+ if path.mod_path().is_some_and(|mod_path| mod_path.is_ident() || mod_path.is_self())
+ {
+ self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent { id });
+ }
+ self.err_ty()
+ }
+ };
+ self.resolver.reset_to_guard(g);
+ ty
+ }
+
fn infer_async_block(
&mut self,
tgt_expr: ExprId,
@@ -1482,107 +1475,6 @@ impl InferenceContext<'_> {
}
}
- 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)).map(|it| it as u32);
- 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)).map(|it| it as u32);
- 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 inferred 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(self.db, None, self.resolver.krate()),
- )
- .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, subs)
- }
- Expr::Underscore => rhs_ty.clone(),
- _ => {
- // `lhs` is a place expression, a unit struct, or an enum variant.
- // LHS of assignment doesn't constitute reads.
- let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none(), ExprIsRead::No);
-
- // 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, CoerceNever::Yes) {
- Ok(ty) => ty,
- Err(_) => {
- self.result.type_mismatches.insert(
- lhs.into(),
- TypeMismatch { expected: rhs_ty.clone(), actual: lhs_ty.clone() },
- );
- // `rhs_ty` is returned so no further type mismatches are
- // reported because of this mismatch.
- rhs_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,
@@ -1706,7 +1598,7 @@ impl InferenceContext<'_> {
Statement::Let { pat, type_ref, initializer, else_branch } => {
let decl_ty = type_ref
.as_ref()
- .map(|tr| this.make_ty(tr))
+ .map(|&tr| this.make_body_ty(tr))
.unwrap_or_else(|| this.table.new_type_var());
let ty = if let Some(expr) = initializer {
@@ -1764,7 +1656,7 @@ impl InferenceContext<'_> {
);
}
}
- Statement::Item => (),
+ Statement::Item(_) => (),
}
}
@@ -2249,7 +2141,8 @@ impl InferenceContext<'_> {
kind_id,
args.next().unwrap(), // `peek()` is `Some(_)`, so guaranteed no panic
self,
- |this, type_ref| this.make_ty(type_ref),
+ &self.body.types,
+ |this, type_ref| this.make_body_ty(type_ref),
|this, c, ty| {
const_or_path_to_chalk(
this.db,