diff options
| author | Gary Guo <gary@garyguo.net> | 2020-05-16 18:44:31 +0100 |
|---|---|---|
| committer | Gary Guo <gary@garyguo.net> | 2020-06-15 00:56:21 +0100 |
| commit | 5393a2995bcea9a927c23d88a921c55bea886771 (patch) | |
| tree | c8b8f290ab7cdf52d91467511bda2ae810c8e1e3 | |
| parent | 06e47688bf15d0215edbe05b21603062f6d2eb5d (diff) | |
| download | rust-5393a2995bcea9a927c23d88a921c55bea886771.tar.gz rust-5393a2995bcea9a927c23d88a921c55bea886771.zip | |
Move convert_place_derefs_to_mutable out from check/method/confirm.rs
This can live inside FnCtxt rather than ConfirmContext, and would be useful for other operations as well.
| -rw-r--r-- | src/librustc_typeck/check/method/confirm.rs | 151 | ||||
| -rw-r--r-- | src/librustc_typeck/check/mod.rs | 1 | ||||
| -rw-r--r-- | src/librustc_typeck/check/reconciliation.rs | 153 |
3 files changed, 157 insertions, 148 deletions
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 007794ce1b7..867dacede6e 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -1,12 +1,12 @@ use super::{probe, MethodCallee}; use crate::astconv::AstConv; -use crate::check::{callee, FnCtxt, Needs, PlaceOp}; +use crate::check::{callee, FnCtxt, Needs}; use crate::hir::def_id::DefId; use crate::hir::GenericArg; use rustc_hir as hir; use rustc_infer::infer::{self, InferOk}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -121,7 +121,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let callee = MethodCallee { def_id: pick.item.def_id, substs: all_substs, sig: method_sig }; if let Some(hir::Mutability::Mut) = pick.autoref { - self.convert_place_derefs_to_mutable(); + self.convert_place_derefs_to_mutable(self.self_expr); } ConfirmResult { callee, illegal_sized_bound } @@ -417,151 +417,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } /////////////////////////////////////////////////////////////////////////// - // RECONCILIATION - - /// When we select a method with a mutable autoref, we have to go convert any - /// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut` - /// respectively. - fn convert_place_derefs_to_mutable(&self) { - // Gather up expressions we want to munge. - let mut exprs = vec![self.self_expr]; - - loop { - match exprs.last().unwrap().kind { - hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) - | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), - _ => break, - } - } - - debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); - - // Fix up autoderefs and derefs. - for (i, &expr) in exprs.iter().rev().enumerate() { - debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); - - // Fix up the autoderefs. Autorefs can only occur immediately preceding - // overloaded place ops, and will be fixed by them in order to get - // the correct region. - let mut source = self.node_ty(expr.hir_id); - // Do not mutate adjustments in place, but rather take them, - // and replace them after mutating them, to avoid having the - // tables borrowed during (`deref_mut`) method resolution. - let previous_adjustments = - self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); - if let Some(mut adjustments) = previous_adjustments { - let needs = Needs::MutPlace; - for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { - if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) { - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { - *deref = OverloadedDeref { region, mutbl }; - } - } - } - source = adjustment.target; - } - self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); - } - - 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.tables.borrow().expr_ty_adjusted(index_expr); - self.convert_place_op_to_mutable( - PlaceOp::Index, - expr, - base_expr, - &[index_expr_ty], - ); - } - hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { - self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); - } - _ => {} - } - } - } - - fn convert_place_op_to_mutable( - &self, - 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); - if !self.tables.borrow().is_method_call(expr) { - debug!("convert_place_op_to_mutable - builtin, nothing to do"); - return; - } - - let base_ty = self - .tables - .borrow() - .expr_adjustments(base_expr) - .last() - .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); - let base_ty = self.resolve_vars_if_possible(&base_ty); - - // Need to deref because overloaded place ops take self by-reference. - let base_ty = - base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty; - - let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op); - let method = match method { - Some(ok) => self.register_infer_ok_obligations(ok), - None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"), - }; - debug!("convert_place_op_to_mutable: method={:?}", method); - self.write_method_call(expr.hir_id, method); - - let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind { - (r, mutbl) - } else { - span_bug!(expr.span, "input to place op is not a ref?"); - }; - - // Convert the autoref in the base expr to mutable with the correct - // region and mutability. - let base_expr_ty = self.node_ty(base_expr.hir_id); - if let Some(adjustments) = - self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) - { - let mut source = base_expr_ty; - for adjustment in &mut adjustments[..] { - if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { - debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded operators. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); - adjustment.target = - self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); - } - source = adjustment.target; - } - - // If we have an autoref followed by unsizing at the end, fix the unsize target. - - if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = - adjustments[..] - { - *target = method.sig.inputs()[0]; - } - } - } - - /////////////////////////////////////////////////////////////////////////// // MISCELLANY fn predicates_require_illegal_sized_bound( diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index fabedc3800a..1e4085f0267 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -79,6 +79,7 @@ pub mod intrinsic; pub mod method; mod op; mod pat; +mod reconciliation; mod regionck; mod upvar; mod wfcheck; diff --git a/src/librustc_typeck/check/reconciliation.rs b/src/librustc_typeck/check/reconciliation.rs new file mode 100644 index 00000000000..b05155ae2ae --- /dev/null +++ b/src/librustc_typeck/check/reconciliation.rs @@ -0,0 +1,153 @@ +use crate::check::{FnCtxt, Needs, PlaceOp}; +use rustc_hir as hir; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; +use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::{self, Ty}; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` + /// into `DerefMut` and `IndexMut` respectively. + /// + /// This is a second pass of typechecking derefs/indices. We need this we do not + /// always know whether a place needs to be mutable or not in the first pass. + /// This happens whether there is an implicit mutable reborrow, e.g. when the type + /// is used as the receiver of a method call. + pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) { + // Gather up expressions we want to munge. + let mut exprs = vec![expr]; + + loop { + match exprs.last().unwrap().kind { + hir::ExprKind::Field(ref expr, _) + | hir::ExprKind::Index(ref expr, _) + | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), + _ => break, + } + } + + debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); + + // Fix up autoderefs and derefs. + for (i, &expr) in exprs.iter().rev().enumerate() { + debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); + + // Fix up the autoderefs. Autorefs can only occur immediately preceding + // overloaded place ops, and will be fixed by them in order to get + // the correct region. + let mut source = self.node_ty(expr.hir_id); + // Do not mutate adjustments in place, but rather take them, + // and replace them after mutating them, to avoid having the + // tables borrowed during (`deref_mut`) method resolution. + let previous_adjustments = + self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); + if let Some(mut adjustments) = previous_adjustments { + let needs = Needs::MutPlace; + for adjustment in &mut adjustments { + if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { + if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) { + let method = self.register_infer_ok_obligations(ok); + if let ty::Ref(region, _, mutbl) = method.sig.output().kind { + *deref = OverloadedDeref { region, mutbl }; + } + } + } + source = adjustment.target; + } + self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); + } + + 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.tables.borrow().expr_ty_adjusted(index_expr); + self.convert_place_op_to_mutable( + PlaceOp::Index, + expr, + base_expr, + &[index_expr_ty], + ); + } + hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { + self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); + } + _ => {} + } + } + } + + fn convert_place_op_to_mutable( + &self, + 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); + if !self.tables.borrow().is_method_call(expr) { + debug!("convert_place_op_to_mutable - builtin, nothing to do"); + return; + } + + let base_ty = self + .tables + .borrow() + .expr_adjustments(base_expr) + .last() + .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); + let base_ty = self.resolve_vars_if_possible(&base_ty); + + // Need to deref because overloaded place ops take self by-reference. + let base_ty = + base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty; + + let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op); + let method = match method { + Some(ok) => self.register_infer_ok_obligations(ok), + None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"), + }; + debug!("convert_place_op_to_mutable: method={:?}", method); + self.write_method_call(expr.hir_id, method); + + let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind { + (r, mutbl) + } else { + span_bug!(expr.span, "input to place op is not a ref?"); + }; + + // Convert the autoref in the base expr to mutable with the correct + // region and mutability. + let base_expr_ty = self.node_ty(base_expr.hir_id); + if let Some(adjustments) = + self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) + { + let mut source = base_expr_ty; + for adjustment in &mut adjustments[..] { + if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { + debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded operators. + allow_two_phase_borrow: AllowTwoPhase::No, + }, + }; + adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); + adjustment.target = + self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); + } + source = adjustment.target; + } + + // If we have an autoref followed by unsizing at the end, fix the unsize target. + + if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = + adjustments[..] + { + *target = method.sig.inputs()[0]; + } + } + } +} |
