From 8b14b743f990d300995f0b1e7147f50512270af7 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 1 Aug 2024 02:20:52 +0900 Subject: [PATCH 1/2] fix: Apply `IndexMut` obligations for non-assigning mutable index usages, too --- .../crates/hir-ty/src/infer/mutability.rs | 27 +++++++++-- .../crates/hir-ty/src/tests/regression.rs | 47 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 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 abb702d15002f..fbc7570c7429a 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; @@ -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/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index b371e5856b083..ac2dfea1010d4 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); + +struct Bar; + +impl Bar { + fn bar(&mut self) {} +} + +impl Foo { + fn new(_v: V) -> Self { + loop {} + } +} + +impl Index for Foo { + type Output = V; + fn index(&self, _index: B) -> &Self::Output { + loop {} + } +} + +impl IndexMut for Foo { + 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(); +} +"#, + ) +} From 086065e56b83e54f1236be0c69fadd613e7363a8 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 1 Aug 2024 02:51:33 +0900 Subject: [PATCH 2/2] Prevent redundant obigation push for assignee exprs --- .../crates/hir-ty/src/infer/mutability.rs | 12 ++++++++---- 1 file changed, 8 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 fbc7570c7429a..66267e08db62b 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 @@ -104,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 @@ -129,12 +129,16 @@ impl InferenceContext<'_> { target, }) = base_adjustments { - *mutability = Mutability::Mut; - if let TyKind::Ref(_, _, ty) = target.kind(Interner) { - base_ty = Some(ty.clone()); + // 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) {