]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/check/place_op.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / place_op.rs
CommitLineData
f035d41b 1use crate::check::method::MethodCallee;
17df50a5 2use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp};
c295e0f8
XL
3use rustc_ast as ast;
4use rustc_errors::Applicability;
f035d41b
XL
5use rustc_hir as hir;
6use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
7use rustc_infer::infer::InferOk;
8use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
9use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
10use rustc_middle::ty::{self, Ty};
11use rustc_span::symbol::{sym, Ident};
12use rustc_span::Span;
13use rustc_trait_selection::autoderef::Autoderef;
f9652781 14use std::slice;
f035d41b
XL
15
16impl<'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}