Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/mutability.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/mutability.rs | 48 |
1 files changed, 39 insertions, 9 deletions
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 00e5eac229..66267e08db 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -1,14 +1,18 @@ //! Finds if an expression is an immutable context or a mutable context, which is used in selecting //! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar. -use chalk_ir::Mutability; +use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp}, lang_item::LangItem, }; -use hir_expand::name; +use hir_expand::name::Name; +use intern::sym; -use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, OverloadedDeref}; +use crate::{ + infer::Expectation, lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Interner, + OverloadedDeref, TyBuilder, TyKind, +}; use super::InferenceContext; @@ -100,7 +104,7 @@ impl InferenceContext<'_> { Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => { self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } - &Expr::Index { base, index, is_assignee_expr: _ } => { + &Expr::Index { base, index, is_assignee_expr } => { if mutability == Mutability::Mut { if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { if let Some(index_trait) = self @@ -108,10 +112,13 @@ impl InferenceContext<'_> { .lang_item(self.table.trait_env.krate, LangItem::IndexMut) .and_then(|l| l.as_trait()) { - if let Some(index_fn) = - self.db.trait_data(index_trait).method_by_name(&name![index_mut]) + if let Some(index_fn) = self + .db + .trait_data(index_trait) + .method_by_name(&Name::new_symbol_root(sym::index_mut.clone())) { *f = index_fn; + let mut base_ty = None; let base_adjustments = self .result .expr_adjustments @@ -119,11 +126,32 @@ impl InferenceContext<'_> { .and_then(|it| it.last_mut()); if let Some(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutability)), - .. + target, }) = base_adjustments { + // For assignee exprs `IndexMut` obiligations are already applied + if !is_assignee_expr { + if let TyKind::Ref(_, _, ty) = target.kind(Interner) { + base_ty = Some(ty.clone()); + } + } *mutability = Mutability::Mut; } + + // Apply `IndexMut` obligation for non-assignee expr + if let Some(base_ty) = base_ty { + let index_ty = + if let Some(ty) = self.result.type_of_expr.get(index) { + ty.clone() + } else { + self.infer_expr(index, &Expectation::none()) + }; + let trait_ref = TyBuilder::trait_ref(self.db, index_trait) + .push(base_ty) + .fill(|_| index_ty.clone().cast(Interner)) + .build(); + self.push_obligation(trait_ref.cast(Interner)); + } } } } @@ -139,8 +167,10 @@ impl InferenceContext<'_> { .lang_item(self.table.trait_env.krate, LangItem::DerefMut) .and_then(|l| l.as_trait()) { - if let Some(deref_fn) = - self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) + if let Some(deref_fn) = self + .db + .trait_data(deref_trait) + .method_by_name(&Name::new_symbol_root(sym::deref_mut.clone())) { *f = deref_fn; } |