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