]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/place_op.rs
Auto merge of #76541 - matthiaskrgr:unstable_sort, r=davidtwco
[rust.git] / compiler / rustc_typeck / src / check / place_op.rs
1 use crate::check::method::MethodCallee;
2 use crate::check::{FnCtxt, PlaceOp};
3 use rustc_hir as hir;
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};
10 use rustc_span::Span;
11 use rustc_trait_selection::autoderef::Autoderef;
12 use std::slice;
13
14 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15     /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
16     pub(super) fn lookup_derefing(
17         &self,
18         expr: &hir::Expr<'_>,
19         oprnd_expr: &'tcx hir::Expr<'tcx>,
20         oprnd_ty: Ty<'tcx>,
21     ) -> Option<Ty<'tcx>> {
22         if let Some(mt) = oprnd_ty.builtin_deref(true) {
23             return Some(mt.ty);
24         }
25
26         let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
27         let method = self.register_infer_ok_obligations(ok);
28         if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
29             self.apply_adjustments(
30                 oprnd_expr,
31                 vec![Adjustment {
32                     kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
33                     target: method.sig.inputs()[0],
34                 }],
35             );
36         } else {
37             span_bug!(expr.span, "input to deref is not a ref?");
38         }
39         let ty = self.make_overloaded_place_return_type(method).ty;
40         self.write_method_call(expr.hir_id, method);
41         Some(ty)
42     }
43
44     /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
45     pub(super) fn lookup_indexing(
46         &self,
47         expr: &hir::Expr<'_>,
48         base_expr: &'tcx hir::Expr<'tcx>,
49         base_ty: Ty<'tcx>,
50         idx_ty: Ty<'tcx>,
51     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
52         // FIXME(#18741) -- this is almost but not quite the same as the
53         // autoderef that normal method probing does. They could likely be
54         // consolidated.
55
56         let mut autoderef = self.autoderef(base_expr.span, base_ty);
57         let mut result = None;
58         while result.is_none() && autoderef.next().is_some() {
59             result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
60         }
61         self.register_predicates(autoderef.into_obligations());
62         result
63     }
64
65     /// To type-check `base_expr[index_expr]`, we progressively autoderef
66     /// (and otherwise adjust) `base_expr`, looking for a type which either
67     /// supports builtin indexing or overloaded indexing.
68     /// This loop implements one step in that search; the autoderef loop
69     /// is implemented by `lookup_indexing`.
70     fn try_index_step(
71         &self,
72         expr: &hir::Expr<'_>,
73         base_expr: &hir::Expr<'_>,
74         autoderef: &Autoderef<'a, 'tcx>,
75         index_ty: Ty<'tcx>,
76     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
77         let adjusted_ty =
78             self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
79         debug!(
80             "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
81              index_ty={:?})",
82             expr, base_expr, adjusted_ty, index_ty
83         );
84
85         for &unsize in &[false, true] {
86             let mut self_ty = adjusted_ty;
87             if unsize {
88                 // We only unsize arrays here.
89                 if let ty::Array(element_ty, _) = adjusted_ty.kind() {
90                     self_ty = self.tcx.mk_slice(element_ty);
91                 } else {
92                     continue;
93                 }
94             }
95
96             // If some lookup succeeds, write callee into table and extract index/element
97             // type from the method signature.
98             // If some lookup succeeded, install method in table
99             let input_ty = self.next_ty_var(TypeVariableOrigin {
100                 kind: TypeVariableOriginKind::AutoDeref,
101                 span: base_expr.span,
102             });
103             let method =
104                 self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);
105
106             let result = method.map(|ok| {
107                 debug!("try_index_step: success, using overloaded indexing");
108                 let method = self.register_infer_ok_obligations(ok);
109
110                 let mut adjustments = self.adjust_steps(autoderef);
111                 if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
112                     adjustments.push(Adjustment {
113                         kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
114                         target: self.tcx.mk_ref(
115                             region,
116                             ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
117                         ),
118                     });
119                 } else {
120                     span_bug!(expr.span, "input to index is not a ref?");
121                 }
122                 if unsize {
123                     adjustments.push(Adjustment {
124                         kind: Adjust::Pointer(PointerCast::Unsize),
125                         target: method.sig.inputs()[0],
126                     });
127                 }
128                 self.apply_adjustments(base_expr, adjustments);
129
130                 self.write_method_call(expr.hir_id, method);
131                 (input_ty, self.make_overloaded_place_return_type(method).ty)
132             });
133             if result.is_some() {
134                 return result;
135             }
136         }
137
138         None
139     }
140
141     /// Try to resolve an overloaded place op. We only deal with the immutable
142     /// variant here (Deref/Index). In some contexts we would need the mutable
143     /// variant (DerefMut/IndexMut); those would be later converted by
144     /// `convert_place_derefs_to_mutable`.
145     pub(super) fn try_overloaded_place_op(
146         &self,
147         span: Span,
148         base_ty: Ty<'tcx>,
149         arg_tys: &[Ty<'tcx>],
150         op: PlaceOp,
151     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
152         debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
153
154         let (imm_tr, imm_op) = match op {
155             PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
156             PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
157         };
158         imm_tr.and_then(|trait_did| {
159             self.lookup_method_in_trait(
160                 span,
161                 Ident::with_dummy_span(imm_op),
162                 trait_did,
163                 base_ty,
164                 Some(arg_tys),
165             )
166         })
167     }
168
169     fn try_mutable_overloaded_place_op(
170         &self,
171         span: Span,
172         base_ty: Ty<'tcx>,
173         arg_tys: &[Ty<'tcx>],
174         op: PlaceOp,
175     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
176         debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
177
178         let (mut_tr, mut_op) = match op {
179             PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
180             PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
181         };
182         mut_tr.and_then(|trait_did| {
183             self.lookup_method_in_trait(
184                 span,
185                 Ident::with_dummy_span(mut_op),
186                 trait_did,
187                 base_ty,
188                 Some(arg_tys),
189             )
190         })
191     }
192
193     /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
194     /// into `DerefMut` and `IndexMut` respectively.
195     ///
196     /// This is a second pass of typechecking derefs/indices. We need this because we do not
197     /// always know whether a place needs to be mutable or not in the first pass.
198     /// This happens whether there is an implicit mutable reborrow, e.g. when the type
199     /// is used as the receiver of a method call.
200     pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
201         // Gather up expressions we want to munge.
202         let mut exprs = vec![expr];
203
204         while let hir::ExprKind::Field(ref expr, _)
205         | hir::ExprKind::Index(ref expr, _)
206         | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) = exprs.last().unwrap().kind
207         {
208             exprs.push(&expr);
209         }
210
211         debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
212
213         // Fix up autoderefs and derefs.
214         let mut inside_union = false;
215         for (i, &expr) in exprs.iter().rev().enumerate() {
216             debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
217
218             let mut source = self.node_ty(expr.hir_id);
219             if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) {
220                 // Clear previous flag; after a pointer indirection it does not apply any more.
221                 inside_union = false;
222             }
223             if source.ty_adt_def().map_or(false, |adt| adt.is_union()) {
224                 inside_union = true;
225             }
226             // Fix up the autoderefs. Autorefs can only occur immediately preceding
227             // overloaded place ops, and will be fixed by them in order to get
228             // the correct region.
229             // Do not mutate adjustments in place, but rather take them,
230             // and replace them after mutating them, to avoid having the
231             // typeck results borrowed during (`deref_mut`) method resolution.
232             let previous_adjustments =
233                 self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
234             if let Some(mut adjustments) = previous_adjustments {
235                 for adjustment in &mut adjustments {
236                     if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
237                         if let Some(ok) = self.try_mutable_overloaded_place_op(
238                             expr.span,
239                             source,
240                             &[],
241                             PlaceOp::Deref,
242                         ) {
243                             let method = self.register_infer_ok_obligations(ok);
244                             if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() {
245                                 *deref = OverloadedDeref { region, mutbl };
246                             }
247                             // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
248                             // This helps avoid accidental drops.
249                             if inside_union
250                                 && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop())
251                             {
252                                 let mut err = self.tcx.sess.struct_span_err(
253                                     expr.span,
254                                     "not automatically applying `DerefMut` on `ManuallyDrop` union field",
255                                 );
256                                 err.help(
257                                     "writing to this reference calls the destructor for the old value",
258                                 );
259                                 err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
260                                 err.emit();
261                             }
262                         }
263                     }
264                     source = adjustment.target;
265                 }
266                 self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
267             }
268
269             match expr.kind {
270                 hir::ExprKind::Index(ref base_expr, ..) => {
271                     self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr);
272                 }
273                 hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
274                     self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr);
275                 }
276                 _ => {}
277             }
278         }
279     }
280
281     fn convert_place_op_to_mutable(
282         &self,
283         op: PlaceOp,
284         expr: &hir::Expr<'_>,
285         base_expr: &hir::Expr<'_>,
286     ) {
287         debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
288         if !self.typeck_results.borrow().is_method_call(expr) {
289             debug!("convert_place_op_to_mutable - builtin, nothing to do");
290             return;
291         }
292
293         // Need to deref because overloaded place ops take self by-reference.
294         let base_ty = self
295             .typeck_results
296             .borrow()
297             .expr_ty_adjusted(base_expr)
298             .builtin_deref(false)
299             .expect("place op takes something that is not a ref")
300             .ty;
301
302         let arg_ty = match op {
303             PlaceOp::Deref => None,
304             PlaceOp::Index => {
305                 // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
306                 // in try_index_step. This is the subst at index 1.
307                 //
308                 // Note: we should *not* use `expr_ty` of index_expr here because autoderef
309                 // during coercions can cause type of index_expr to differ from `T` (#72002).
310                 // We also could not use `expr_ty_adjusted` of index_expr because reborrowing
311                 // during coercions can also cause type of index_expr to differ from `T`,
312                 // which can potentially cause regionck failure (#74933).
313                 Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1))
314             }
315         };
316         let arg_tys = match arg_ty {
317             None => &[],
318             Some(ref ty) => slice::from_ref(ty),
319         };
320
321         let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
322         let method = match method {
323             Some(ok) => self.register_infer_ok_obligations(ok),
324             // Couldn't find the mutable variant of the place op, keep the
325             // current, immutable version.
326             None => return,
327         };
328         debug!("convert_place_op_to_mutable: method={:?}", method);
329         self.write_method_call(expr.hir_id, method);
330
331         let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() {
332             r
333         } else {
334             span_bug!(expr.span, "input to mutable place op is not a mut ref?");
335         };
336
337         // Convert the autoref in the base expr to mutable with the correct
338         // region and mutability.
339         let base_expr_ty = self.node_ty(base_expr.hir_id);
340         if let Some(adjustments) =
341             self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
342         {
343             let mut source = base_expr_ty;
344             for adjustment in &mut adjustments[..] {
345                 if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
346                     debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
347                     let mutbl = AutoBorrowMutability::Mut {
348                         // Deref/indexing can be desugared to a method call,
349                         // so maybe we could use two-phase here.
350                         // See the documentation of AllowTwoPhase for why that's
351                         // not the case today.
352                         allow_two_phase_borrow: AllowTwoPhase::No,
353                     };
354                     adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
355                     adjustment.target =
356                         self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
357                 }
358                 source = adjustment.target;
359             }
360
361             // If we have an autoref followed by unsizing at the end, fix the unsize target.
362             if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
363                 adjustments[..]
364             {
365                 *target = method.sig.inputs()[0];
366             }
367         }
368     }
369 }