1 use crate::check::method::MethodCallee;
2 use crate::check::{FnCtxt, PlaceOp};
4 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
5 use rustc_infer::infer::InferOk;
6 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
7 use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
8 use rustc_middle::ty::{self, Ty};
9 use rustc_span::symbol::{sym, Ident};
11 use rustc_trait_selection::autoderef::Autoderef;
13 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14 /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
15 pub(super) fn lookup_derefing(
18 oprnd_expr: &'tcx hir::Expr<'tcx>,
20 ) -> Option<Ty<'tcx>> {
21 if let Some(mt) = oprnd_ty.builtin_deref(true) {
25 let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
26 let method = self.register_infer_ok_obligations(ok);
27 if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
28 self.apply_adjustments(
31 kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
32 target: method.sig.inputs()[0],
36 span_bug!(expr.span, "input to deref is not a ref?");
38 let ty = self.make_overloaded_place_return_type(method).ty;
39 self.write_method_call(expr.hir_id, method);
43 /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
44 pub(super) fn lookup_indexing(
47 base_expr: &'tcx hir::Expr<'tcx>,
50 ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
51 // FIXME(#18741) -- this is almost but not quite the same as the
52 // autoderef that normal method probing does. They could likely be
55 let mut autoderef = self.autoderef(base_expr.span, base_ty);
56 let mut result = None;
57 while result.is_none() && autoderef.next().is_some() {
58 result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
60 self.register_predicates(autoderef.into_obligations());
64 /// To type-check `base_expr[index_expr]`, we progressively autoderef
65 /// (and otherwise adjust) `base_expr`, looking for a type which either
66 /// supports builtin indexing or overloaded indexing.
67 /// This loop implements one step in that search; the autoderef loop
68 /// is implemented by `lookup_indexing`.
72 base_expr: &hir::Expr<'_>,
73 autoderef: &Autoderef<'a, 'tcx>,
75 ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
77 self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
79 "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
81 expr, base_expr, adjusted_ty, index_ty
84 for &unsize in &[false, true] {
85 let mut self_ty = adjusted_ty;
87 // We only unsize arrays here.
88 if let ty::Array(element_ty, _) = adjusted_ty.kind {
89 self_ty = self.tcx.mk_slice(element_ty);
95 // If some lookup succeeds, write callee into table and extract index/element
96 // type from the method signature.
97 // If some lookup succeeded, install method in table
98 let input_ty = self.next_ty_var(TypeVariableOrigin {
99 kind: TypeVariableOriginKind::AutoDeref,
100 span: base_expr.span,
103 self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);
105 let result = method.map(|ok| {
106 debug!("try_index_step: success, using overloaded indexing");
107 let method = self.register_infer_ok_obligations(ok);
109 let mut adjustments = self.adjust_steps(autoderef);
110 if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
111 adjustments.push(Adjustment {
112 kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
113 target: self.tcx.mk_ref(
115 ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
119 span_bug!(expr.span, "input to index is not a ref?");
122 adjustments.push(Adjustment {
123 kind: Adjust::Pointer(PointerCast::Unsize),
124 target: method.sig.inputs()[0],
127 self.apply_adjustments(base_expr, adjustments);
129 self.write_method_call(expr.hir_id, method);
130 (input_ty, self.make_overloaded_place_return_type(method).ty)
132 if result.is_some() {
140 /// Try to resolve an overloaded place op. We only deal with the immutable
141 /// variant here (Deref/Index). In some contexts we would need the mutable
142 /// variant (DerefMut/IndexMut); those would be later converted by
143 /// `convert_place_derefs_to_mutable`.
144 pub(super) fn try_overloaded_place_op(
148 arg_tys: &[Ty<'tcx>],
150 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
151 debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
153 let (imm_tr, imm_op) = match op {
154 PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
155 PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
157 imm_tr.and_then(|trait_did| {
158 self.lookup_method_in_trait(
160 Ident::with_dummy_span(imm_op),
168 fn try_mutable_overloaded_place_op(
172 arg_tys: &[Ty<'tcx>],
174 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
175 debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
177 let (mut_tr, mut_op) = match op {
178 PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
179 PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
181 mut_tr.and_then(|trait_did| {
182 self.lookup_method_in_trait(
184 Ident::with_dummy_span(mut_op),
192 /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
193 /// into `DerefMut` and `IndexMut` respectively.
195 /// This is a second pass of typechecking derefs/indices. We need this we do not
196 /// always know whether a place needs to be mutable or not in the first pass.
197 /// This happens whether there is an implicit mutable reborrow, e.g. when the type
198 /// is used as the receiver of a method call.
199 pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
200 // Gather up expressions we want to munge.
201 let mut exprs = vec![expr];
203 while let hir::ExprKind::Field(ref expr, _)
204 | hir::ExprKind::Index(ref expr, _)
205 | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) = exprs.last().unwrap().kind
210 debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
212 // Fix up autoderefs and derefs.
213 for (i, &expr) in exprs.iter().rev().enumerate() {
214 debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
216 // Fix up the autoderefs. Autorefs can only occur immediately preceding
217 // overloaded place ops, and will be fixed by them in order to get
218 // the correct region.
219 let mut source = self.node_ty(expr.hir_id);
220 // Do not mutate adjustments in place, but rather take them,
221 // and replace them after mutating them, to avoid having the
222 // typeck results borrowed during (`deref_mut`) method resolution.
223 let previous_adjustments =
224 self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
225 if let Some(mut adjustments) = previous_adjustments {
226 for adjustment in &mut adjustments {
227 if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
228 if let Some(ok) = self.try_mutable_overloaded_place_op(
234 let method = self.register_infer_ok_obligations(ok);
235 if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
236 *deref = OverloadedDeref { region, mutbl };
240 source = adjustment.target;
242 self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
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(
257 hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
258 self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
265 fn convert_place_op_to_mutable(
268 expr: &hir::Expr<'_>,
269 base_expr: &hir::Expr<'_>,
270 arg_tys: &[Ty<'tcx>],
272 debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
273 if !self.typeck_results.borrow().is_method_call(expr) {
274 debug!("convert_place_op_to_mutable - builtin, nothing to do");
278 // Need to deref because overloaded place ops take self by-reference.
282 .expr_ty_adjusted(base_expr)
283 .builtin_deref(false)
284 .expect("place op takes something that is not a ref")
287 let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
288 let method = match method {
289 Some(ok) => self.register_infer_ok_obligations(ok),
290 // Couldn't find the mutable variant of the place op, keep the
291 // current, immutable version.
294 debug!("convert_place_op_to_mutable: method={:?}", method);
295 self.write_method_call(expr.hir_id, method);
297 let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
300 span_bug!(expr.span, "input to mutable place op is not a mut ref?");
303 // Convert the autoref in the base expr to mutable with the correct
304 // region and mutability.
305 let base_expr_ty = self.node_ty(base_expr.hir_id);
306 if let Some(adjustments) =
307 self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
309 let mut source = base_expr_ty;
310 for adjustment in &mut adjustments[..] {
311 if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
312 debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
313 let mutbl = AutoBorrowMutability::Mut {
314 // Deref/indexing can be desugared to a method call,
315 // so maybe we could use two-phase here.
316 // See the documentation of AllowTwoPhase for why that's
317 // not the case today.
318 allow_two_phase_borrow: AllowTwoPhase::No,
320 adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
322 self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
324 source = adjustment.target;
327 // If we have an autoref followed by unsizing at the end, fix the unsize target.
328 if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
331 *target = method.sig.inputs()[0];