Skip to content

Commit 9ad83de

Browse files
committed
Support overloaded index MIR lowering
1 parent eb4939e commit 9ad83de

File tree

4 files changed

+169
-3
lines changed

4 files changed

+169
-3
lines changed

crates/hir-ty/src/consteval/tests.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use base_db::fixture::WithFixture;
22
use hir_def::db::DefDatabase;
33

44
use crate::{
5-
consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner,
5+
consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
6+
Interner,
67
};
78

89
use super::{
@@ -30,7 +31,12 @@ fn check_number(ra_fixture: &str, answer: i128) {
3031
match &r.data(Interner).value {
3132
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
3233
ConstScalar::Bytes(b, _) => {
33-
assert_eq!(b, &answer.to_le_bytes()[0..b.len()]);
34+
assert_eq!(
35+
b,
36+
&answer.to_le_bytes()[0..b.len()],
37+
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
38+
i128::from_le_bytes(pad16(b, true))
39+
);
3440
}
3541
x => panic!("Expected number but found {:?}", x),
3642
},
@@ -215,6 +221,42 @@ fn overloaded_deref_autoref() {
215221
);
216222
}
217223

224+
#[test]
225+
fn overloaded_index() {
226+
check_number(
227+
r#"
228+
//- minicore: index
229+
struct Foo;
230+
231+
impl core::ops::Index<usize> for Foo {
232+
type Output = i32;
233+
fn index(&self, index: usize) -> &i32 {
234+
if index == 7 {
235+
&700
236+
} else {
237+
&1000
238+
}
239+
}
240+
}
241+
242+
impl core::ops::IndexMut<usize> for Foo {
243+
fn index_mut(&mut self, index: usize) -> &mut i32 {
244+
if index == 7 {
245+
&mut 7
246+
} else {
247+
&mut 10
248+
}
249+
}
250+
}
251+
252+
const GOAL: i32 = {
253+
(Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7])
254+
};
255+
"#,
256+
3417,
257+
);
258+
}
259+
218260
#[test]
219261
fn function_call() {
220262
check_number(

crates/hir-ty/src/infer/mutability.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,21 @@ impl<'a> InferenceContext<'a> {
9595
self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread))
9696
}
9797
&Expr::Index { base, index } => {
98+
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
99+
if mutability == Mutability::Mut {
100+
if let Some(index_trait) = self
101+
.db
102+
.lang_item(self.table.trait_env.krate, LangItem::IndexMut)
103+
.and_then(|l| l.as_trait())
104+
{
105+
if let Some(index_fn) =
106+
self.db.trait_data(index_trait).method_by_name(&name![index_mut])
107+
{
108+
*f = index_fn;
109+
}
110+
}
111+
}
112+
}
98113
self.infer_mut_expr(base, mutability);
99114
self.infer_mut_expr(index, Mutability::Not);
100115
}

crates/hir-ty/src/mir/lower/as_place.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! MIR lowering for places
22
33
use super::*;
4+
use hir_def::FunctionId;
45
use hir_expand::name;
56

67
macro_rules! not_supported {
@@ -193,7 +194,24 @@ impl MirLowerCtx<'_> {
193194
if index_ty != TyBuilder::usize()
194195
|| !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
195196
{
196-
not_supported!("overloaded index");
197+
let Some(index_fn) = self.infer.method_resolution(expr_id) else {
198+
return Err(MirLowerError::UnresolvedMethod);
199+
};
200+
let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
201+
return Ok(None);
202+
};
203+
let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else {
204+
return Ok(None);
205+
};
206+
return self.lower_overloaded_index(
207+
current,
208+
base_place,
209+
self.expr_ty_after_adjustments(*base),
210+
self.expr_ty(expr_id),
211+
index_operand,
212+
expr_id.into(),
213+
index_fn,
214+
);
197215
}
198216
let Some((mut p_base, current)) =
199217
self.lower_expr_as_place(current, *base, true)? else {
@@ -210,6 +228,49 @@ impl MirLowerCtx<'_> {
210228
}
211229
}
212230

231+
fn lower_overloaded_index(
232+
&mut self,
233+
current: BasicBlockId,
234+
place: Place,
235+
base_ty: Ty,
236+
result_ty: Ty,
237+
index_operand: Operand,
238+
span: MirSpan,
239+
index_fn: (FunctionId, Substitution),
240+
) -> Result<Option<(Place, BasicBlockId)>> {
241+
let is_mutable = 'b: {
242+
if let Some(index_mut_trait) = self.resolve_lang_item(LangItem::IndexMut)?.as_trait() {
243+
if let Some(index_mut_fn) =
244+
self.db.trait_data(index_mut_trait).method_by_name(&name![index_mut])
245+
{
246+
break 'b index_mut_fn == index_fn.0;
247+
}
248+
}
249+
false
250+
};
251+
let (mutability, borrow_kind) = match is_mutable {
252+
true => (Mutability::Mut, BorrowKind::Mut { allow_two_phase_borrow: false }),
253+
false => (Mutability::Not, BorrowKind::Shared),
254+
};
255+
let base_ref = TyKind::Ref(mutability, static_lifetime(), base_ty).intern(Interner);
256+
let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner);
257+
let ref_place: Place = self.temp(base_ref)?.into();
258+
self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
259+
let mut result: Place = self.temp(result_ref)?.into();
260+
let index_fn_op = Operand::const_zst(
261+
TyKind::FnDef(
262+
self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(),
263+
index_fn.1,
264+
)
265+
.intern(Interner),
266+
);
267+
let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false)? else {
268+
return Ok(None);
269+
};
270+
result.projection.push(ProjectionElem::Deref);
271+
Ok(Some((result, current)))
272+
}
273+
213274
fn lower_overloaded_deref(
214275
&mut self,
215276
current: BasicBlockId,

crates/ide-diagnostics/src/handlers/mutability_errors.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,54 @@ fn f(x: [(i32, u8); 10]) {
564564
);
565565
}
566566

567+
#[test]
568+
fn overloaded_index() {
569+
check_diagnostics(
570+
r#"
571+
//- minicore: index
572+
use core::ops::{Index, IndexMut};
573+
574+
struct Foo;
575+
impl Index<usize> for Foo {
576+
type Output = (i32, u8);
577+
fn index(&self, index: usize) -> &(i32, u8) {
578+
&(5, 2)
579+
}
580+
}
581+
impl IndexMut<usize> for Foo {
582+
fn index_mut(&mut self, index: usize) -> &mut (i32, u8) {
583+
&mut (5, 2)
584+
}
585+
}
586+
fn f() {
587+
let mut x = Foo;
588+
//^^^^^ 💡 weak: variable does not need to be mutable
589+
let y = &x[2];
590+
let x = Foo;
591+
let y = &mut x[2];
592+
//^^^^ 💡 error: cannot mutate immutable variable `x`
593+
let mut x = &mut Foo;
594+
//^^^^^ 💡 weak: variable does not need to be mutable
595+
let y: &mut (i32, u8) = &mut x[2];
596+
let x = Foo;
597+
let ref mut y = x[7];
598+
//^^^^ 💡 error: cannot mutate immutable variable `x`
599+
let (ref mut y, _) = x[3];
600+
//^^^^ 💡 error: cannot mutate immutable variable `x`
601+
match x[10] {
602+
//^^^^^ 💡 error: cannot mutate immutable variable `x`
603+
(ref y, _) => (),
604+
(_, ref mut y) => (),
605+
}
606+
let mut x = Foo;
607+
let mut i = 5;
608+
//^^^^^ 💡 weak: variable does not need to be mutable
609+
let y = &mut x[i];
610+
}
611+
"#,
612+
);
613+
}
614+
567615
#[test]
568616
fn overloaded_deref() {
569617
check_diagnostics(

0 commit comments

Comments
 (0)