Skip to content

Commit 5393a29

Browse files
committed
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.
1 parent 06e4768 commit 5393a29

File tree

3 files changed

+157
-148
lines changed

3 files changed

+157
-148
lines changed

src/librustc_typeck/check/method/confirm.rs

+3-148
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use super::{probe, MethodCallee};
22

33
use crate::astconv::AstConv;
4-
use crate::check::{callee, FnCtxt, Needs, PlaceOp};
4+
use crate::check::{callee, FnCtxt, Needs};
55
use crate::hir::def_id::DefId;
66
use crate::hir::GenericArg;
77
use rustc_hir as hir;
88
use rustc_infer::infer::{self, InferOk};
9-
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
9+
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
1010
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
1111
use rustc_middle::ty::fold::TypeFoldable;
1212
use rustc_middle::ty::subst::{Subst, SubstsRef};
@@ -121,7 +121,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
121121
let callee = MethodCallee { def_id: pick.item.def_id, substs: all_substs, sig: method_sig };
122122

123123
if let Some(hir::Mutability::Mut) = pick.autoref {
124-
self.convert_place_derefs_to_mutable();
124+
self.convert_place_derefs_to_mutable(self.self_expr);
125125
}
126126

127127
ConfirmResult { callee, illegal_sized_bound }
@@ -416,151 +416,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
416416
self.register_wf_obligation(fty.into(), self.span, traits::MiscObligation);
417417
}
418418

419-
///////////////////////////////////////////////////////////////////////////
420-
// RECONCILIATION
421-
422-
/// When we select a method with a mutable autoref, we have to go convert any
423-
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
424-
/// respectively.
425-
fn convert_place_derefs_to_mutable(&self) {
426-
// Gather up expressions we want to munge.
427-
let mut exprs = vec![self.self_expr];
428-
429-
loop {
430-
match exprs.last().unwrap().kind {
431-
hir::ExprKind::Field(ref expr, _)
432-
| hir::ExprKind::Index(ref expr, _)
433-
| hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
434-
_ => break,
435-
}
436-
}
437-
438-
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
439-
440-
// Fix up autoderefs and derefs.
441-
for (i, &expr) in exprs.iter().rev().enumerate() {
442-
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
443-
444-
// Fix up the autoderefs. Autorefs can only occur immediately preceding
445-
// overloaded place ops, and will be fixed by them in order to get
446-
// the correct region.
447-
let mut source = self.node_ty(expr.hir_id);
448-
// Do not mutate adjustments in place, but rather take them,
449-
// and replace them after mutating them, to avoid having the
450-
// tables borrowed during (`deref_mut`) method resolution.
451-
let previous_adjustments =
452-
self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
453-
if let Some(mut adjustments) = previous_adjustments {
454-
let needs = Needs::MutPlace;
455-
for adjustment in &mut adjustments {
456-
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
457-
if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) {
458-
let method = self.register_infer_ok_obligations(ok);
459-
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
460-
*deref = OverloadedDeref { region, mutbl };
461-
}
462-
}
463-
}
464-
source = adjustment.target;
465-
}
466-
self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
467-
}
468-
469-
match expr.kind {
470-
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
471-
// We need to get the final type in case dereferences were needed for the trait
472-
// to apply (#72002).
473-
let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
474-
self.convert_place_op_to_mutable(
475-
PlaceOp::Index,
476-
expr,
477-
base_expr,
478-
&[index_expr_ty],
479-
);
480-
}
481-
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
482-
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
483-
}
484-
_ => {}
485-
}
486-
}
487-
}
488-
489-
fn convert_place_op_to_mutable(
490-
&self,
491-
op: PlaceOp,
492-
expr: &hir::Expr<'_>,
493-
base_expr: &hir::Expr<'_>,
494-
arg_tys: &[Ty<'tcx>],
495-
) {
496-
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
497-
if !self.tables.borrow().is_method_call(expr) {
498-
debug!("convert_place_op_to_mutable - builtin, nothing to do");
499-
return;
500-
}
501-
502-
let base_ty = self
503-
.tables
504-
.borrow()
505-
.expr_adjustments(base_expr)
506-
.last()
507-
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
508-
let base_ty = self.resolve_vars_if_possible(&base_ty);
509-
510-
// Need to deref because overloaded place ops take self by-reference.
511-
let base_ty =
512-
base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty;
513-
514-
let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op);
515-
let method = match method {
516-
Some(ok) => self.register_infer_ok_obligations(ok),
517-
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"),
518-
};
519-
debug!("convert_place_op_to_mutable: method={:?}", method);
520-
self.write_method_call(expr.hir_id, method);
521-
522-
let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind {
523-
(r, mutbl)
524-
} else {
525-
span_bug!(expr.span, "input to place op is not a ref?");
526-
};
527-
528-
// Convert the autoref in the base expr to mutable with the correct
529-
// region and mutability.
530-
let base_expr_ty = self.node_ty(base_expr.hir_id);
531-
if let Some(adjustments) =
532-
self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
533-
{
534-
let mut source = base_expr_ty;
535-
for adjustment in &mut adjustments[..] {
536-
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
537-
debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
538-
let mutbl = match mutbl {
539-
hir::Mutability::Not => AutoBorrowMutability::Not,
540-
hir::Mutability::Mut => AutoBorrowMutability::Mut {
541-
// For initial two-phase borrow
542-
// deployment, conservatively omit
543-
// overloaded operators.
544-
allow_two_phase_borrow: AllowTwoPhase::No,
545-
},
546-
};
547-
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
548-
adjustment.target =
549-
self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
550-
}
551-
source = adjustment.target;
552-
}
553-
554-
// If we have an autoref followed by unsizing at the end, fix the unsize target.
555-
556-
if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
557-
adjustments[..]
558-
{
559-
*target = method.sig.inputs()[0];
560-
}
561-
}
562-
}
563-
564419
///////////////////////////////////////////////////////////////////////////
565420
// MISCELLANY
566421

src/librustc_typeck/check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub mod intrinsic;
7979
pub mod method;
8080
mod op;
8181
mod pat;
82+
mod reconciliation;
8283
mod regionck;
8384
mod upvar;
8485
mod wfcheck;
+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use crate::check::{FnCtxt, Needs, PlaceOp};
2+
use rustc_hir as hir;
3+
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
4+
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
5+
use rustc_middle::ty::{self, Ty};
6+
7+
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8+
/// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
9+
/// into `DerefMut` and `IndexMut` respectively.
10+
///
11+
/// This is a second pass of typechecking derefs/indices. We need this we do not
12+
/// always know whether a place needs to be mutable or not in the first pass.
13+
/// This happens whether there is an implicit mutable reborrow, e.g. when the type
14+
/// is used as the receiver of a method call.
15+
pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
16+
// Gather up expressions we want to munge.
17+
let mut exprs = vec![expr];
18+
19+
loop {
20+
match exprs.last().unwrap().kind {
21+
hir::ExprKind::Field(ref expr, _)
22+
| hir::ExprKind::Index(ref expr, _)
23+
| hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
24+
_ => break,
25+
}
26+
}
27+
28+
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
29+
30+
// Fix up autoderefs and derefs.
31+
for (i, &expr) in exprs.iter().rev().enumerate() {
32+
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
33+
34+
// Fix up the autoderefs. Autorefs can only occur immediately preceding
35+
// overloaded place ops, and will be fixed by them in order to get
36+
// the correct region.
37+
let mut source = self.node_ty(expr.hir_id);
38+
// Do not mutate adjustments in place, but rather take them,
39+
// and replace them after mutating them, to avoid having the
40+
// tables borrowed during (`deref_mut`) method resolution.
41+
let previous_adjustments =
42+
self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
43+
if let Some(mut adjustments) = previous_adjustments {
44+
let needs = Needs::MutPlace;
45+
for adjustment in &mut adjustments {
46+
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
47+
if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) {
48+
let method = self.register_infer_ok_obligations(ok);
49+
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
50+
*deref = OverloadedDeref { region, mutbl };
51+
}
52+
}
53+
}
54+
source = adjustment.target;
55+
}
56+
self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
57+
}
58+
59+
match expr.kind {
60+
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
61+
// We need to get the final type in case dereferences were needed for the trait
62+
// to apply (#72002).
63+
let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
64+
self.convert_place_op_to_mutable(
65+
PlaceOp::Index,
66+
expr,
67+
base_expr,
68+
&[index_expr_ty],
69+
);
70+
}
71+
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
72+
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
73+
}
74+
_ => {}
75+
}
76+
}
77+
}
78+
79+
fn convert_place_op_to_mutable(
80+
&self,
81+
op: PlaceOp,
82+
expr: &hir::Expr<'_>,
83+
base_expr: &hir::Expr<'_>,
84+
arg_tys: &[Ty<'tcx>],
85+
) {
86+
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
87+
if !self.tables.borrow().is_method_call(expr) {
88+
debug!("convert_place_op_to_mutable - builtin, nothing to do");
89+
return;
90+
}
91+
92+
let base_ty = self
93+
.tables
94+
.borrow()
95+
.expr_adjustments(base_expr)
96+
.last()
97+
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
98+
let base_ty = self.resolve_vars_if_possible(&base_ty);
99+
100+
// Need to deref because overloaded place ops take self by-reference.
101+
let base_ty =
102+
base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty;
103+
104+
let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op);
105+
let method = match method {
106+
Some(ok) => self.register_infer_ok_obligations(ok),
107+
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"),
108+
};
109+
debug!("convert_place_op_to_mutable: method={:?}", method);
110+
self.write_method_call(expr.hir_id, method);
111+
112+
let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind {
113+
(r, mutbl)
114+
} else {
115+
span_bug!(expr.span, "input to place op is not a ref?");
116+
};
117+
118+
// Convert the autoref in the base expr to mutable with the correct
119+
// region and mutability.
120+
let base_expr_ty = self.node_ty(base_expr.hir_id);
121+
if let Some(adjustments) =
122+
self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
123+
{
124+
let mut source = base_expr_ty;
125+
for adjustment in &mut adjustments[..] {
126+
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
127+
debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
128+
let mutbl = match mutbl {
129+
hir::Mutability::Not => AutoBorrowMutability::Not,
130+
hir::Mutability::Mut => AutoBorrowMutability::Mut {
131+
// For initial two-phase borrow
132+
// deployment, conservatively omit
133+
// overloaded operators.
134+
allow_two_phase_borrow: AllowTwoPhase::No,
135+
},
136+
};
137+
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
138+
adjustment.target =
139+
self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
140+
}
141+
source = adjustment.target;
142+
}
143+
144+
// If we have an autoref followed by unsizing at the end, fix the unsize target.
145+
146+
if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
147+
adjustments[..]
148+
{
149+
*target = method.sig.inputs()[0];
150+
}
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)