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`.
This commit is contained in:
Yuki Okushi 2020-08-12 12:07:08 +09:00 committed by GitHub
commit 43babed7e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 13 deletions

View file

@ -9,6 +9,7 @@ use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::autoderef::Autoderef;
use std::slice;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
@ -243,19 +244,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
match expr.kind {
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
// We need to get the final type in case dereferences were needed for the trait
// to apply (#72002).
let index_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(index_expr);
self.convert_place_op_to_mutable(
PlaceOp::Index,
expr,
base_expr,
&[index_expr_ty],
);
hir::ExprKind::Index(ref base_expr, ..) => {
self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr);
}
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr);
}
_ => {}
}
@ -267,9 +260,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
op: PlaceOp,
expr: &hir::Expr<'_>,
base_expr: &hir::Expr<'_>,
arg_tys: &[Ty<'tcx>],
) {
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
if !self.typeck_results.borrow().is_method_call(expr) {
debug!("convert_place_op_to_mutable - builtin, nothing to do");
return;
@ -284,6 +276,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.expect("place op takes something that is not a ref")
.ty;
let arg_ty = match op {
PlaceOp::Deref => None,
PlaceOp::Index => {
// We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
// in try_index_step. This is the subst at index 1.
//
// Note: we should *not* use `expr_ty` of index_expr here because autoderef
// during coercions can cause type of index_expr to differ from `T` (#72002).
// We also could not use `expr_ty_adjusted` of index_expr because reborrowing
// during coercions can also cause type of index_expr to differ from `T`,
// which can potentially cause regionck failure (#74933).
Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1))
}
};
let arg_tys = match arg_ty {
None => &[],
Some(ref ty) => slice::from_ref(ty),
};
let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
let method = match method {
Some(ok) => self.register_infer_ok_obligations(ok),

View file

@ -0,0 +1,38 @@
// check-pass
//
// rust-lang/rust#74933: Lifetime error when indexing with borrowed index
use std::ops::{Index, IndexMut};
struct S(V);
struct K<'a>(&'a ());
struct V;
impl<'a> Index<&'a K<'a>> for S {
type Output = V;
fn index(&self, _: &'a K<'a>) -> &V {
&self.0
}
}
impl<'a> IndexMut<&'a K<'a>> for S {
fn index_mut(&mut self, _: &'a K<'a>) -> &mut V {
&mut self.0
}
}
impl V {
fn foo(&mut self) {}
}
fn test(s: &mut S, k: &K<'_>) {
s[k] = V;
s[k].foo();
}
fn main() {
let mut s = S(V);
let k = K(&());
test(&mut s, &k);
}