]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/place_op.rs
Rollup merge of #72954 - hermitcore:rwlock, r=dtolnay
[rust.git] / src / librustc_typeck / 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
13 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14     /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
15     pub(super) fn lookup_derefing(
16         &self,
17         expr: &hir::Expr<'_>,
18         oprnd_expr: &'tcx hir::Expr<'tcx>,
19         oprnd_ty: Ty<'tcx>,
20     ) -> Option<Ty<'tcx>> {
21         if let Some(mt) = oprnd_ty.builtin_deref(true) {
22             return Some(mt.ty);
23         }
24
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(
29                 oprnd_expr,
30                 vec![Adjustment {
31                     kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
32                     target: method.sig.inputs()[0],
33                 }],
34             );
35         } else {
36             span_bug!(expr.span, "input to deref is not a ref?");
37         }
38         let ty = self.make_overloaded_place_return_type(method).ty;
39         self.write_method_call(expr.hir_id, method);
40         Some(ty)
41     }
42
43     /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
44     pub(super) fn lookup_indexing(
45         &self,
46         expr: &hir::Expr<'_>,
47         base_expr: &'tcx hir::Expr<'tcx>,
48         base_ty: Ty<'tcx>,
49         idx_ty: Ty<'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
53         // consolidated.
54
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);
59         }
60         self.register_predicates(autoderef.into_obligations());
61         result
62     }
63
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`.
69     fn try_index_step(
70         &self,
71         expr: &hir::Expr<'_>,
72         base_expr: &hir::Expr<'_>,
73         autoderef: &Autoderef<'a, 'tcx>,
74         index_ty: Ty<'tcx>,
75     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
76         let adjusted_ty =
77             self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
78         debug!(
79             "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
80              index_ty={:?})",
81             expr, base_expr, adjusted_ty, index_ty
82         );
83
84         for &unsize in &[false, true] {
85             let mut self_ty = adjusted_ty;
86             if unsize {
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);
90                 } else {
91                     continue;
92                 }
93             }
94
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,
101             });
102             let method =
103                 self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);
104
105             let result = method.map(|ok| {
106                 debug!("try_index_step: success, using overloaded indexing");
107                 let method = self.register_infer_ok_obligations(ok);
108
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(
114                             region,
115                             ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
116                         ),
117                     });
118                 } else {
119                     span_bug!(expr.span, "input to index is not a ref?");
120                 }
121                 if unsize {
122                     adjustments.push(Adjustment {
123                         kind: Adjust::Pointer(PointerCast::Unsize),
124                         target: method.sig.inputs()[0],
125                     });
126                 }
127                 self.apply_adjustments(base_expr, adjustments);
128
129                 self.write_method_call(expr.hir_id, method);
130                 (input_ty, self.make_overloaded_place_return_type(method).ty)
131             });
132             if result.is_some() {
133                 return result;
134             }
135         }
136
137         None
138     }
139
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(
145         &self,
146         span: Span,
147         base_ty: Ty<'tcx>,
148         arg_tys: &[Ty<'tcx>],
149         op: PlaceOp,
150     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
151         debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
152
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),
156         };
157         imm_tr.and_then(|trait_did| {
158             self.lookup_method_in_trait(
159                 span,
160                 Ident::with_dummy_span(imm_op),
161                 trait_did,
162                 base_ty,
163                 Some(arg_tys),
164             )
165         })
166     }
167
168     fn try_mutable_overloaded_place_op(
169         &self,
170         span: Span,
171         base_ty: Ty<'tcx>,
172         arg_tys: &[Ty<'tcx>],
173         op: PlaceOp,
174     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
175         debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
176
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),
180         };
181         mut_tr.and_then(|trait_did| {
182             self.lookup_method_in_trait(
183                 span,
184                 Ident::with_dummy_span(mut_op),
185                 trait_did,
186                 base_ty,
187                 Some(arg_tys),
188             )
189         })
190     }
191
192     /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
193     /// into `DerefMut` and `IndexMut` respectively.
194     ///
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];
202
203         loop {
204             match exprs.last().unwrap().kind {
205                 hir::ExprKind::Field(ref expr, _)
206                 | hir::ExprKind::Index(ref expr, _)
207                 | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
208                 _ => break,
209             }
210         }
211
212         debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
213
214         // Fix up autoderefs and derefs.
215         for (i, &expr) in exprs.iter().rev().enumerate() {
216             debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
217
218             // Fix up the autoderefs. Autorefs can only occur immediately preceding
219             // overloaded place ops, and will be fixed by them in order to get
220             // the correct region.
221             let mut source = self.node_ty(expr.hir_id);
222             // Do not mutate adjustments in place, but rather take them,
223             // and replace them after mutating them, to avoid having the
224             // typeck results borrowed during (`deref_mut`) method resolution.
225             let previous_adjustments =
226                 self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
227             if let Some(mut adjustments) = previous_adjustments {
228                 for adjustment in &mut adjustments {
229                     if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
230                         if let Some(ok) = self.try_mutable_overloaded_place_op(
231                             expr.span,
232                             source,
233                             &[],
234                             PlaceOp::Deref,
235                         ) {
236                             let method = self.register_infer_ok_obligations(ok);
237                             if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
238                                 *deref = OverloadedDeref { region, mutbl };
239                             }
240                         }
241                     }
242                     source = adjustment.target;
243                 }
244                 self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
245             }
246
247             match expr.kind {
248                 hir::ExprKind::Index(ref base_expr, ref index_expr) => {
249                     // We need to get the final type in case dereferences were needed for the trait
250                     // to apply (#72002).
251                     let index_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(index_expr);
252                     self.convert_place_op_to_mutable(
253                         PlaceOp::Index,
254                         expr,
255                         base_expr,
256                         &[index_expr_ty],
257                     );
258                 }
259                 hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
260                     self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
261                 }
262                 _ => {}
263             }
264         }
265     }
266
267     fn convert_place_op_to_mutable(
268         &self,
269         op: PlaceOp,
270         expr: &hir::Expr<'_>,
271         base_expr: &hir::Expr<'_>,
272         arg_tys: &[Ty<'tcx>],
273     ) {
274         debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
275         if !self.typeck_results.borrow().is_method_call(expr) {
276             debug!("convert_place_op_to_mutable - builtin, nothing to do");
277             return;
278         }
279
280         // Need to deref because overloaded place ops take self by-reference.
281         let base_ty = self
282             .typeck_results
283             .borrow()
284             .expr_ty_adjusted(base_expr)
285             .builtin_deref(false)
286             .expect("place op takes something that is not a ref")
287             .ty;
288
289         let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
290         let method = match method {
291             Some(ok) => self.register_infer_ok_obligations(ok),
292             // Couldn't find the mutable variant of the place op, keep the
293             // current, immutable version.
294             None => return,
295         };
296         debug!("convert_place_op_to_mutable: method={:?}", method);
297         self.write_method_call(expr.hir_id, method);
298
299         let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
300             r
301         } else {
302             span_bug!(expr.span, "input to mutable place op is not a mut ref?");
303         };
304
305         // Convert the autoref in the base expr to mutable with the correct
306         // region and mutability.
307         let base_expr_ty = self.node_ty(base_expr.hir_id);
308         if let Some(adjustments) =
309             self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
310         {
311             let mut source = base_expr_ty;
312             for adjustment in &mut adjustments[..] {
313                 if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
314                     debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
315                     let mutbl = AutoBorrowMutability::Mut {
316                         // Deref/indexing can be desugared to a method call,
317                         // so maybe we could use two-phase here.
318                         // See the documentation of AllowTwoPhase for why that's
319                         // not the case today.
320                         allow_two_phase_borrow: AllowTwoPhase::No,
321                     };
322                     adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
323                     adjustment.target =
324                         self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
325                 }
326                 source = adjustment.target;
327             }
328
329             // If we have an autoref followed by unsizing at the end, fix the unsize target.
330             if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
331                 adjustments[..]
332             {
333                 *target = method.sig.inputs()[0];
334             }
335         }
336     }
337 }