about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-31 18:06:53 +0000
committerbors <bors@rust-lang.org>2024-07-31 18:06:53 +0000
commite12408bf1145a0339e37d73259c36999e078eb44 (patch)
tree918b917af71cca7fb44575d5a150aaa75f10842d
parentbba84aa8366c9e44082f0da471a80fcf249b0433 (diff)
parent086065e56b83e54f1236be0c69fadd613e7363a8 (diff)
downloadrust-e12408bf1145a0339e37d73259c36999e078eb44.tar.gz
rust-e12408bf1145a0339e37d73259c36999e078eb44.zip
Auto merge of #17755 - ShoyuVanilla:issue-17738, r=davidbarsky
fix: Apply `IndexMut` obligations for non-assigning mutable index usages

Fixes #17738

Currently, we are pushing `IndexMut` obligations only for assign usages;
https://github.com/rust-lang/rust-analyzer/blob/f982f3fa2c23570c10108e83c1ecc392ea411866/crates/hir-ty/src/infer/expr.rs#L809-L817
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs47
2 files changed, 76 insertions, 4 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index abb702d1500..66267e08db6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -1,7 +1,7 @@
 //! 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,
@@ -9,7 +9,10 @@ use hir_def::{
 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;
 
@@ -101,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
@@ -115,6 +118,7 @@ impl InferenceContext<'_> {
                                 .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
@@ -122,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));
+                                }
                             }
                         }
                     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index b371e5856b0..ac2dfea1010 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -2075,3 +2075,50 @@ impl<'a, T> Trait<'a> for &'a T {
 "#,
     )
 }
+
+#[test]
+fn issue_17738() {
+    check_types(
+        r#"
+//- minicore: index
+use core::ops::{Index, IndexMut};
+
+struct Foo<K, V>(K, V);
+
+struct Bar;
+
+impl Bar {
+    fn bar(&mut self) {}
+}
+
+impl<K, V> Foo<K, V> {
+    fn new(_v: V) -> Self {
+        loop {}
+    }
+}
+
+impl<K, B, V> Index<B> for Foo<K, V> {
+    type Output = V;
+    fn index(&self, _index: B) -> &Self::Output {
+        loop {}
+    }
+}
+
+impl<K, V> IndexMut<K> for Foo<K, V> {
+    fn index_mut(&mut self, _index: K) -> &mut Self::Output {
+        loop {}
+    }
+}
+
+fn test() {
+    let mut t1 = Foo::new(Bar);
+     // ^^^^^^ Foo<&'? (), Bar>
+    t1[&()] = Bar;
+
+    let mut t2 = Foo::new(Bar);
+     // ^^^^^^ Foo<&'? (), Bar>
+    t2[&()].bar();
+}
+"#,
+    )
+}