Skip to content

Commit e12408b

Browse files
committed
Auto merge of rust-lang#17755 - ShoyuVanilla:issue-17738, r=davidbarsky
fix: Apply `IndexMut` obligations for non-assigning mutable index usages Fixes rust-lang#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
2 parents bba84aa + 086065e commit e12408b

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
//! Finds if an expression is an immutable context or a mutable context, which is used in selecting
22
//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
33
4-
use chalk_ir::Mutability;
4+
use chalk_ir::{cast::Cast, Mutability};
55
use hir_def::{
66
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
77
lang_item::LangItem,
88
};
99
use hir_expand::name::Name;
1010
use intern::sym;
1111

12-
use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, OverloadedDeref};
12+
use crate::{
13+
infer::Expectation, lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Interner,
14+
OverloadedDeref, TyBuilder, TyKind,
15+
};
1316

1417
use super::InferenceContext;
1518

@@ -101,7 +104,7 @@ impl InferenceContext<'_> {
101104
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
102105
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
103106
}
104-
&Expr::Index { base, index, is_assignee_expr: _ } => {
107+
&Expr::Index { base, index, is_assignee_expr } => {
105108
if mutability == Mutability::Mut {
106109
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
107110
if let Some(index_trait) = self
@@ -115,18 +118,40 @@ impl InferenceContext<'_> {
115118
.method_by_name(&Name::new_symbol_root(sym::index_mut.clone()))
116119
{
117120
*f = index_fn;
121+
let mut base_ty = None;
118122
let base_adjustments = self
119123
.result
120124
.expr_adjustments
121125
.get_mut(&base)
122126
.and_then(|it| it.last_mut());
123127
if let Some(Adjustment {
124128
kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
125-
..
129+
target,
126130
}) = base_adjustments
127131
{
132+
// For assignee exprs `IndexMut` obiligations are already applied
133+
if !is_assignee_expr {
134+
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
135+
base_ty = Some(ty.clone());
136+
}
137+
}
128138
*mutability = Mutability::Mut;
129139
}
140+
141+
// Apply `IndexMut` obligation for non-assignee expr
142+
if let Some(base_ty) = base_ty {
143+
let index_ty =
144+
if let Some(ty) = self.result.type_of_expr.get(index) {
145+
ty.clone()
146+
} else {
147+
self.infer_expr(index, &Expectation::none())
148+
};
149+
let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
150+
.push(base_ty)
151+
.fill(|_| index_ty.clone().cast(Interner))
152+
.build();
153+
self.push_obligation(trait_ref.cast(Interner));
154+
}
130155
}
131156
}
132157
}

src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs

+47
Original file line numberDiff line numberDiff line change
@@ -2075,3 +2075,50 @@ impl<'a, T> Trait<'a> for &'a T {
20752075
"#,
20762076
)
20772077
}
2078+
2079+
#[test]
2080+
fn issue_17738() {
2081+
check_types(
2082+
r#"
2083+
//- minicore: index
2084+
use core::ops::{Index, IndexMut};
2085+
2086+
struct Foo<K, V>(K, V);
2087+
2088+
struct Bar;
2089+
2090+
impl Bar {
2091+
fn bar(&mut self) {}
2092+
}
2093+
2094+
impl<K, V> Foo<K, V> {
2095+
fn new(_v: V) -> Self {
2096+
loop {}
2097+
}
2098+
}
2099+
2100+
impl<K, B, V> Index<B> for Foo<K, V> {
2101+
type Output = V;
2102+
fn index(&self, _index: B) -> &Self::Output {
2103+
loop {}
2104+
}
2105+
}
2106+
2107+
impl<K, V> IndexMut<K> for Foo<K, V> {
2108+
fn index_mut(&mut self, _index: K) -> &mut Self::Output {
2109+
loop {}
2110+
}
2111+
}
2112+
2113+
fn test() {
2114+
let mut t1 = Foo::new(Bar);
2115+
// ^^^^^^ Foo<&'? (), Bar>
2116+
t1[&()] = Bar;
2117+
2118+
let mut t2 = Foo::new(Bar);
2119+
// ^^^^^^ Foo<&'? (), Bar>
2120+
t2[&()].bar();
2121+
}
2122+
"#,
2123+
)
2124+
}

0 commit comments

Comments
 (0)