1use rustc_errors::Applicability;
2use rustc_hir_analysis::autoderef::Autoderef;
3use rustc_infer::infer::InferOk;
4use rustc_infer::traits::{Obligation, ObligationCauseCode};
5use rustc_middle::span_bug;
6use rustc_middle::ty::adjustment::{
7 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref,
8 PointerCoercion,
9};
10use rustc_middle::ty::{self, Ty};
11use rustc_span::{Ident, Span, sym};
12use tracing::debug;
13use {rustc_ast as ast, rustc_hir as hir};
14
15use crate::method::MethodCallee;
16use crate::{FnCtxt, PlaceOp};
17
18impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19 pub(super) fn lookup_derefing(
21 &self,
22 expr: &hir::Expr<'_>,
23 oprnd_expr: &'tcx hir::Expr<'tcx>,
24 oprnd_ty: Ty<'tcx>,
25 ) -> Option<Ty<'tcx>> {
26 if let Some(ty) = oprnd_ty.builtin_deref(true) {
27 return Some(ty);
28 }
29
30 let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
31 let method = self.register_infer_ok_obligations(ok);
32 if let ty::Ref(_, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
33 self.apply_adjustments(
34 oprnd_expr,
35 vec![Adjustment {
36 kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)),
37 target: method.sig.inputs()[0],
38 }],
39 );
40 } else {
41 span_bug!(expr.span, "input to deref is not a ref?");
42 }
43 let ty = self.make_overloaded_place_return_type(method);
44 self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
45 Some(ty)
46 }
47
48 pub(super) fn lookup_indexing(
50 &self,
51 expr: &hir::Expr<'_>,
52 base_expr: &'tcx hir::Expr<'tcx>,
53 base_ty: Ty<'tcx>,
54 index_expr: &'tcx hir::Expr<'tcx>,
55 idx_ty: Ty<'tcx>,
56 ) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
57 let mut autoderef = self.autoderef(base_expr.span, base_ty);
62 let mut result = None;
63 while result.is_none() && autoderef.next().is_some() {
64 result = self.try_index_step(expr, base_expr, &autoderef, idx_ty, index_expr);
65 }
66 self.register_predicates(autoderef.into_obligations());
67 result
68 }
69
70 fn negative_index(
71 &self,
72 ty: Ty<'tcx>,
73 span: Span,
74 base_expr: &hir::Expr<'_>,
75 ) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
76 let ty = self.resolve_vars_if_possible(ty);
77 let mut err = self.dcx().struct_span_err(
78 span,
79 format!("negative integers cannot be used to index on a `{ty}`"),
80 );
81 err.span_label(span, format!("cannot use a negative integer for indexing on `{ty}`"));
82 if let (hir::ExprKind::Path(..), Ok(snippet)) =
83 (&base_expr.kind, self.tcx.sess.source_map().span_to_snippet(base_expr.span))
84 {
85 err.span_suggestion_verbose(
87 span.shrink_to_lo(),
88 format!(
89 "to access an element starting from the end of the `{ty}`, compute the index",
90 ),
91 format!("{snippet}.len() "),
92 Applicability::MachineApplicable,
93 );
94 }
95 let reported = err.emit();
96 Some((Ty::new_error(self.tcx, reported), Ty::new_error(self.tcx, reported)))
97 }
98
99 fn try_index_step(
105 &self,
106 expr: &hir::Expr<'_>,
107 base_expr: &hir::Expr<'_>,
108 autoderef: &Autoderef<'a, 'tcx>,
109 index_ty: Ty<'tcx>,
110 index_expr: &hir::Expr<'_>,
111 ) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
112 let adjusted_ty =
113 self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
114 debug!(
115 "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
116 index_ty={:?})",
117 expr, base_expr, adjusted_ty, index_ty
118 );
119
120 if let hir::ExprKind::Unary(
121 hir::UnOp::Neg,
122 hir::Expr {
123 kind: hir::ExprKind::Lit(hir::Lit { node: ast::LitKind::Int(..), .. }),
124 ..
125 },
126 ) = index_expr.kind
127 {
128 match adjusted_ty.kind() {
129 ty::Adt(def, _) if self.tcx.is_diagnostic_item(sym::Vec, def.did()) => {
130 return self.negative_index(adjusted_ty, index_expr.span, base_expr);
131 }
132 ty::Slice(_) | ty::Array(_, _) => {
133 return self.negative_index(adjusted_ty, index_expr.span, base_expr);
134 }
135 _ => {}
136 }
137 }
138
139 for unsize in [false, true] {
140 let mut self_ty = adjusted_ty;
141 if unsize {
142 if let ty::Array(element_ty, ct) = *adjusted_ty.kind() {
144 self.register_predicate(Obligation::new(
145 self.tcx,
146 self.cause(base_expr.span, ObligationCauseCode::ArrayLen(adjusted_ty)),
147 self.param_env,
148 ty::ClauseKind::ConstArgHasType(ct, self.tcx.types.usize),
149 ));
150 self_ty = Ty::new_slice(self.tcx, element_ty);
151 } else {
152 continue;
153 }
154 }
155
156 let input_ty = self.next_ty_var(base_expr.span);
160 let method =
161 self.try_overloaded_place_op(expr.span, self_ty, Some(input_ty), PlaceOp::Index);
162
163 if let Some(result) = method {
164 debug!("try_index_step: success, using overloaded indexing");
165 let method = self.register_infer_ok_obligations(result);
166
167 let mut adjustments = self.adjust_steps(autoderef);
168 if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
169 adjustments.push(Adjustment {
170 kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)),
171 target: Ty::new_imm_ref(self.tcx, *region, adjusted_ty),
172 });
173 } else {
174 span_bug!(expr.span, "input to index is not a ref?");
175 }
176 if unsize {
177 adjustments.push(Adjustment {
178 kind: Adjust::Pointer(PointerCoercion::Unsize),
179 target: method.sig.inputs()[0],
180 });
181 }
182 self.apply_adjustments(base_expr, adjustments);
183
184 self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
185
186 return Some((input_ty, self.make_overloaded_place_return_type(method)));
187 }
188 }
189
190 None
191 }
192
193 pub(super) fn try_overloaded_place_op(
198 &self,
199 span: Span,
200 base_ty: Ty<'tcx>,
201 opt_rhs_ty: Option<Ty<'tcx>>,
202 op: PlaceOp,
203 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
204 debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
205
206 let (Some(imm_tr), imm_op) = (match op {
207 PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
208 PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
209 }) else {
210 return None;
212 };
213
214 self.lookup_method_in_trait(
215 self.misc(span),
216 Ident::with_dummy_span(imm_op),
217 imm_tr,
218 base_ty,
219 opt_rhs_ty,
220 )
221 }
222
223 fn try_mutable_overloaded_place_op(
224 &self,
225 span: Span,
226 base_ty: Ty<'tcx>,
227 opt_rhs_ty: Option<Ty<'tcx>>,
228 op: PlaceOp,
229 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
230 debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
231
232 let (Some(mut_tr), mut_op) = (match op {
233 PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
234 PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
235 }) else {
236 return None;
238 };
239
240 self.lookup_method_in_trait(
241 self.misc(span),
242 Ident::with_dummy_span(mut_op),
243 mut_tr,
244 base_ty,
245 opt_rhs_ty,
246 )
247 }
248
249 pub(crate) fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
257 let mut exprs = vec![expr];
259
260 while let hir::ExprKind::Field(expr, _)
261 | hir::ExprKind::Index(expr, _, _)
262 | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = exprs.last().unwrap().kind
263 {
264 exprs.push(expr);
265 }
266
267 debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
268
269 let mut inside_union = false;
271 for (i, &expr) in exprs.iter().rev().enumerate() {
272 debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
273
274 let mut source = self.node_ty(expr.hir_id);
275 if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _)) {
276 inside_union = false;
278 }
279 if source.is_union() {
280 inside_union = true;
281 }
282 let previous_adjustments =
289 self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
290 if let Some(mut adjustments) = previous_adjustments {
291 for adjustment in &mut adjustments {
292 if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind
293 && let Some(ok) = self.try_mutable_overloaded_place_op(
294 expr.span,
295 source,
296 None,
297 PlaceOp::Deref,
298 )
299 {
300 let method = self.register_infer_ok_obligations(ok);
301 let ty::Ref(_, _, mutbl) = *method.sig.output().kind() else {
302 span_bug!(
303 self.tcx.def_span(method.def_id),
304 "expected DerefMut to return a &mut"
305 );
306 };
307 *deref = OverloadedDeref { mutbl, span: deref.span };
308 self.enforce_context_effects(None, expr.span, method.def_id, method.args);
309 if inside_union
312 && source.ty_adt_def().is_some_and(|adt| adt.is_manually_drop())
313 {
314 self.dcx().struct_span_err(
315 expr.span,
316 "not automatically applying `DerefMut` on `ManuallyDrop` union field",
317 )
318 .with_help(
319 "writing to this reference calls the destructor for the old value",
320 )
321 .with_help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor")
322 .emit();
323 }
324 }
325 source = adjustment.target;
326 }
327 self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
328 }
329
330 match expr.kind {
331 hir::ExprKind::Index(base_expr, ..) => {
332 self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr);
333 }
334 hir::ExprKind::Unary(hir::UnOp::Deref, base_expr) => {
335 self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr);
336 }
337 _ => {}
338 }
339 }
340 }
341
342 fn convert_place_op_to_mutable(
343 &self,
344 op: PlaceOp,
345 expr: &hir::Expr<'_>,
346 base_expr: &hir::Expr<'_>,
347 ) {
348 debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
349 if !self.typeck_results.borrow().is_method_call(expr) {
350 debug!("convert_place_op_to_mutable - builtin, nothing to do");
351 return;
352 }
353
354 let base_ty = self
356 .typeck_results
357 .borrow()
358 .expr_ty_adjusted(base_expr)
359 .builtin_deref(false)
360 .expect("place op takes something that is not a ref");
361
362 let arg_ty = match op {
363 PlaceOp::Deref => None,
364 PlaceOp::Index => {
365 Some(self.typeck_results.borrow().node_args(expr.hir_id).type_at(1))
374 }
375 };
376 let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_ty, op);
377 let method = match method {
378 Some(ok) => self.register_infer_ok_obligations(ok),
379 None => return,
382 };
383 debug!("convert_place_op_to_mutable: method={:?}", method);
384 self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
385
386 let ty::Ref(region, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() else {
387 span_bug!(expr.span, "input to mutable place op is not a mut ref?");
388 };
389
390 let base_expr_ty = self.node_ty(base_expr.hir_id);
393 if let Some(adjustments) =
394 self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
395 {
396 let mut source = base_expr_ty;
397 for adjustment in &mut adjustments[..] {
398 if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
399 debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
400 let mutbl = AutoBorrowMutability::Mut {
401 allow_two_phase_borrow: AllowTwoPhase::No,
406 };
407 adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(mutbl));
408 adjustment.target = Ty::new_ref(self.tcx, *region, source, mutbl.into());
409 }
410 source = adjustment.target;
411 }
412
413 if let [
415 ..,
416 Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
417 Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), ref mut target },
418 ] = adjustments[..]
419 {
420 *target = method.sig.inputs()[0];
421 }
422 }
423 }
424}