Skip to content

Commit 43babed

Browse files
authored
Rollup merge of #74960 - nbdd0121:typeck, r=nikomatsakis
Fix regionck failure when converting Index to IndexMut Fixes #74933 Consider an overloaded index expression `base[index]`. Without knowing whether it will be mutated, this will initially be desugared into `<U as Index<T>>::index(&base, index)` for some `U` and `T`. Let `V` be the `expr_ty_adjusted` of `index`. If this expression ends up being used in any mutable context (or used in a function call with `&mut self` receiver before #72280), we will need to fix it up. The current code will rewrite it to `<U as IndexMut<V>>::index_mut(&mut base, index)`. In most cases this is fine as `V` will be equal to `T`, however this is not always true when `V/T` are references, as they may have different region. This issue is quite subtle before #72280 as this code path is only used to fixup function receivers, but after #72280 we've made this a common path. The solution is basically just rewrite it to `<U as IndexMut<T>>::index_mut(&mut base, index)`. `T` can retrieved in the fixup path using `node_substs`.
2 parents 7e503a0 + 000f5cd commit 43babed

File tree

2 files changed

+62
-13
lines changed

2 files changed

+62
-13
lines changed

src/librustc_typeck/check/place_op.rs

+24-13
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_middle::ty::{self, Ty};
99
use rustc_span::symbol::{sym, Ident};
1010
use rustc_span::Span;
1111
use rustc_trait_selection::autoderef::Autoderef;
12+
use std::slice;
1213

1314
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1415
/// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
@@ -243,19 +244,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
243244
}
244245

245246
match expr.kind {
246-
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
247-
// We need to get the final type in case dereferences were needed for the trait
248-
// to apply (#72002).
249-
let index_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(index_expr);
250-
self.convert_place_op_to_mutable(
251-
PlaceOp::Index,
252-
expr,
253-
base_expr,
254-
&[index_expr_ty],
255-
);
247+
hir::ExprKind::Index(ref base_expr, ..) => {
248+
self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr);
256249
}
257250
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
258-
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
251+
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr);
259252
}
260253
_ => {}
261254
}
@@ -267,9 +260,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
267260
op: PlaceOp,
268261
expr: &hir::Expr<'_>,
269262
base_expr: &hir::Expr<'_>,
270-
arg_tys: &[Ty<'tcx>],
271263
) {
272-
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
264+
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
273265
if !self.typeck_results.borrow().is_method_call(expr) {
274266
debug!("convert_place_op_to_mutable - builtin, nothing to do");
275267
return;
@@ -284,6 +276,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
284276
.expect("place op takes something that is not a ref")
285277
.ty;
286278

279+
let arg_ty = match op {
280+
PlaceOp::Deref => None,
281+
PlaceOp::Index => {
282+
// We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
283+
// in try_index_step. This is the subst at index 1.
284+
//
285+
// Note: we should *not* use `expr_ty` of index_expr here because autoderef
286+
// during coercions can cause type of index_expr to differ from `T` (#72002).
287+
// We also could not use `expr_ty_adjusted` of index_expr because reborrowing
288+
// during coercions can also cause type of index_expr to differ from `T`,
289+
// which can potentially cause regionck failure (#74933).
290+
Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1))
291+
}
292+
};
293+
let arg_tys = match arg_ty {
294+
None => &[],
295+
Some(ref ty) => slice::from_ref(ty),
296+
};
297+
287298
let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
288299
let method = match method {
289300
Some(ok) => self.register_infer_ok_obligations(ok),

src/test/ui/typeck/issue-74933.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// check-pass
2+
//
3+
// rust-lang/rust#74933: Lifetime error when indexing with borrowed index
4+
5+
use std::ops::{Index, IndexMut};
6+
7+
struct S(V);
8+
struct K<'a>(&'a ());
9+
struct V;
10+
11+
impl<'a> Index<&'a K<'a>> for S {
12+
type Output = V;
13+
14+
fn index(&self, _: &'a K<'a>) -> &V {
15+
&self.0
16+
}
17+
}
18+
19+
impl<'a> IndexMut<&'a K<'a>> for S {
20+
fn index_mut(&mut self, _: &'a K<'a>) -> &mut V {
21+
&mut self.0
22+
}
23+
}
24+
25+
impl V {
26+
fn foo(&mut self) {}
27+
}
28+
29+
fn test(s: &mut S, k: &K<'_>) {
30+
s[k] = V;
31+
s[k].foo();
32+
}
33+
34+
fn main() {
35+
let mut s = S(V);
36+
let k = K(&());
37+
test(&mut s, &k);
38+
}

0 commit comments

Comments
 (0)