Unnamed repository; edit this file 'description' to name the repository.
fix: Apply `IndexMut` obligations for non-assigning mutable index usages, too
Shoyu Vanilla 2024-08-01
parent f982f3f · commit 20e2623
-rw-r--r--crates/hir-ty/src/infer/mutability.rs27
-rw-r--r--crates/hir-ty/src/tests/regression.rs47
2 files changed, 71 insertions, 3 deletions
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index abb702d150..fbc7570c74 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/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;
@@ -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,10 +126,27 @@ impl InferenceContext<'_> {
.and_then(|it| it.last_mut());
if let Some(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
- ..
+ target,
}) = base_adjustments
{
*mutability = Mutability::Mut;
+ if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
+ base_ty = Some(ty.clone());
+ }
+ }
+
+ 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/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index b371e5856b..ac2dfea101 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/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();
+}
+"#,
+ )
+}