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:
commit
43babed7e2
2 changed files with 62 additions and 13 deletions
|
@ -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),
|
||||
|
|
38
src/test/ui/typeck/issue-74933.rs
Normal file
38
src/test/ui/typeck/issue-74933.rs
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue