]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_typeck/src/check/place_op.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.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 if let Some(result) = method {
107 debug!("try_index_step: success, using overloaded indexing");
108 let method = self.register_infer_ok_obligations(result);
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
132 return Some((input_ty, self.make_overloaded_place_return_type(method).ty));
133 }
134 }
135
136 None
137 }
138
139 /// Try to resolve an overloaded place op. We only deal with the immutable
140 /// variant here (Deref/Index). In some contexts we would need the mutable
141 /// variant (DerefMut/IndexMut); those would be later converted by
142 /// `convert_place_derefs_to_mutable`.
143 pub(super) fn try_overloaded_place_op(
144 &self,
145 span: Span,
146 base_ty: Ty<'tcx>,
147 arg_tys: &[Ty<'tcx>],
148 op: PlaceOp,
149 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
150 debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
151
152 let (imm_tr, imm_op) = match op {
153 PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
154 PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
155 };
156 imm_tr.and_then(|trait_did| {
157 self.lookup_method_in_trait(
158 span,
159 Ident::with_dummy_span(imm_op),
160 trait_did,
161 base_ty,
162 Some(arg_tys),
163 )
164 })
165 }
166
167 fn try_mutable_overloaded_place_op(
168 &self,
169 span: Span,
170 base_ty: Ty<'tcx>,
171 arg_tys: &[Ty<'tcx>],
172 op: PlaceOp,
173 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
174 debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
175
176 let (mut_tr, mut_op) = match op {
177 PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
178 PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
179 };
180 mut_tr.and_then(|trait_did| {
181 self.lookup_method_in_trait(
182 span,
183 Ident::with_dummy_span(mut_op),
184 trait_did,
185 base_ty,
186 Some(arg_tys),
187 )
188 })
189 }
190
191 /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
192 /// into `DerefMut` and `IndexMut` respectively.
193 ///
194 /// This is a second pass of typechecking derefs/indices. We need this because we do not
195 /// always know whether a place needs to be mutable or not in the first pass.
196 /// This happens whether there is an implicit mutable reborrow, e.g. when the type
197 /// is used as the receiver of a method call.
198 pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
199 // Gather up expressions we want to munge.
200 let mut exprs = vec![expr];
201
202 while let hir::ExprKind::Field(ref expr, _)
203 | hir::ExprKind::Index(ref expr, _)
204 | hir::ExprKind::Unary(hir::UnOp::Deref, ref expr) = exprs.last().unwrap().kind
205 {
206 exprs.push(&expr);
207 }
208
209 debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
210
211 // Fix up autoderefs and derefs.
212 let mut inside_union = false;
213 for (i, &expr) in exprs.iter().rev().enumerate() {
214 debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
215
216 let mut source = self.node_ty(expr.hir_id);
217 if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _)) {
218 // Clear previous flag; after a pointer indirection it does not apply any more.
219 inside_union = false;
220 }
221 if source.ty_adt_def().map_or(false, |adt| adt.is_union()) {
222 inside_union = true;
223 }
224 // Fix up the autoderefs. Autorefs can only occur immediately preceding
225 // overloaded place ops, and will be fixed by them in order to get
226 // the correct region.
227 // Do not mutate adjustments in place, but rather take them,
228 // and replace them after mutating them, to avoid having the
229 // typeck results borrowed during (`deref_mut`) method resolution.
230 let previous_adjustments =
231 self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
232 if let Some(mut adjustments) = previous_adjustments {
233 for adjustment in &mut adjustments {
234 if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
235 if let Some(ok) = self.try_mutable_overloaded_place_op(
236 expr.span,
237 source,
238 &[],
239 PlaceOp::Deref,
240 ) {
241 let method = self.register_infer_ok_obligations(ok);
242 if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() {
243 *deref = OverloadedDeref { region, mutbl, span: deref.span };
244 }
245 // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
246 // This helps avoid accidental drops.
247 if inside_union
248 && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop())
249 {
250 let mut err = self.tcx.sess.struct_span_err(
251 expr.span,
252 "not automatically applying `DerefMut` on `ManuallyDrop` union field",
253 );
254 err.help(
255 "writing to this reference calls the destructor for the old value",
256 );
257 err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
258 err.emit();
259 }
260 }
261 }
262 source = adjustment.target;
263 }
264 self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
265 }
266
267 match expr.kind {
268 hir::ExprKind::Index(ref base_expr, ..) => {
269 self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr);
270 }
271 hir::ExprKind::Unary(hir::UnOp::Deref, ref base_expr) => {
272 self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr);
273 }
274 _ => {}
275 }
276 }
277 }
278
279 fn convert_place_op_to_mutable(
280 &self,
281 op: PlaceOp,
282 expr: &hir::Expr<'_>,
283 base_expr: &hir::Expr<'_>,
284 ) {
285 debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
286 if !self.typeck_results.borrow().is_method_call(expr) {
287 debug!("convert_place_op_to_mutable - builtin, nothing to do");
288 return;
289 }
290
291 // Need to deref because overloaded place ops take self by-reference.
292 let base_ty = self
293 .typeck_results
294 .borrow()
295 .expr_ty_adjusted(base_expr)
296 .builtin_deref(false)
297 .expect("place op takes something that is not a ref")
298 .ty;
299
300 let arg_ty = match op {
301 PlaceOp::Deref => None,
302 PlaceOp::Index => {
303 // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
304 // in try_index_step. This is the subst at index 1.
305 //
306 // Note: we should *not* use `expr_ty` of index_expr here because autoderef
307 // during coercions can cause type of index_expr to differ from `T` (#72002).
308 // We also could not use `expr_ty_adjusted` of index_expr because reborrowing
309 // during coercions can also cause type of index_expr to differ from `T`,
310 // which can potentially cause regionck failure (#74933).
311 Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1))
312 }
313 };
314 let arg_tys = match arg_ty {
315 None => &[],
316 Some(ref ty) => slice::from_ref(ty),
317 };
318
319 let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
320 let method = match method {
321 Some(ok) => self.register_infer_ok_obligations(ok),
322 // Couldn't find the mutable variant of the place op, keep the
323 // current, immutable version.
324 None => return,
325 };
326 debug!("convert_place_op_to_mutable: method={:?}", method);
327 self.write_method_call(expr.hir_id, method);
328
329 let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() {
330 r
331 } else {
332 span_bug!(expr.span, "input to mutable place op is not a mut ref?");
333 };
334
335 // Convert the autoref in the base expr to mutable with the correct
336 // region and mutability.
337 let base_expr_ty = self.node_ty(base_expr.hir_id);
338 if let Some(adjustments) =
339 self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
340 {
341 let mut source = base_expr_ty;
342 for adjustment in &mut adjustments[..] {
343 if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
344 debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
345 let mutbl = AutoBorrowMutability::Mut {
346 // Deref/indexing can be desugared to a method call,
347 // so maybe we could use two-phase here.
348 // See the documentation of AllowTwoPhase for why that's
349 // not the case today.
350 allow_two_phase_borrow: AllowTwoPhase::No,
351 };
352 adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
353 adjustment.target =
354 self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
355 }
356 source = adjustment.target;
357 }
358
359 // If we have an autoref followed by unsizing at the end, fix the unsize target.
360 if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
361 adjustments[..]
362 {
363 *target = method.sig.inputs()[0];
364 }
365 }
366 }
367 }