1use hir_def::hir::ExprId;
4use rustc_ast_ir::Mutability;
5use rustc_type_ir::inherent::{IntoKind, Ty as _};
6use tracing::debug;
7
8use crate::{
9 Adjust, Adjustment, AutoBorrow, PointerCast,
10 autoderef::InferenceContextAutoderef,
11 infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, unify::InferenceTable},
12 method_resolution::{MethodCallee, TreatNotYetDefinedOpaques},
13 next_solver::{
14 ClauseKind, Ty, TyKind,
15 infer::{
16 InferOk,
17 traits::{Obligation, ObligationCause},
18 },
19 },
20};
21
22#[derive(Debug, Copy, Clone)]
23pub(super) enum PlaceOp {
24 Deref,
25 Index,
26}
27
28impl<'db> InferenceContext<'db> {
29 pub(super) fn try_overloaded_deref(
30 &self,
31 expr: ExprId,
32 base_ty: Ty<'db>,
33 ) -> Option<InferOk<'db, MethodCallee<'db>>> {
34 self.try_overloaded_place_op(expr, base_ty, None, PlaceOp::Deref)
35 }
36
37 fn make_overloaded_place_return_type(&self, method: MethodCallee<'db>) -> Ty<'db> {
42 let ret_ty = method.sig.output();
44
45 ret_ty.builtin_deref(true).unwrap()
47 }
48
49 pub(super) fn lookup_derefing(
51 &mut self,
52 expr: ExprId,
53 oprnd_expr: ExprId,
54 oprnd_ty: Ty<'db>,
55 ) -> Option<Ty<'db>> {
56 if let Some(ty) = oprnd_ty.builtin_deref(true) {
57 return Some(ty);
58 }
59
60 let ok = self.try_overloaded_deref(expr, oprnd_ty)?;
61 let method = self.table.register_infer_ok(ok);
62 if let TyKind::Ref(_, _, Mutability::Not) = method.sig.inputs_and_output.inputs()[0].kind()
63 {
64 self.write_expr_adj(
65 oprnd_expr,
66 Box::new([Adjustment {
67 kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)),
68 target: method.sig.inputs_and_output.inputs()[0].store(),
69 }]),
70 );
71 } else {
72 panic!("input to deref is not a ref?");
73 }
74 let ty = self.make_overloaded_place_return_type(method);
75 self.write_method_resolution(expr, method.def_id, method.args);
76 Some(ty)
77 }
78
79 pub(super) fn lookup_indexing(
81 &mut self,
82 expr: ExprId,
83 base_expr: ExprId,
84 index_expr: ExprId,
85 base_ty: Ty<'db>,
86 idx_ty: Ty<'db>,
87 ) -> Option<(Ty<'db>, Ty<'db>)> {
88 let mut autoderef =
93 InferenceContextAutoderef::new_from_inference_context(self, base_ty, base_expr.into());
94 let mut result = None;
95 while result.is_none() && autoderef.next().is_some() {
96 result = Self::try_index_step(expr, base_expr, index_expr, &mut autoderef, idx_ty);
97 }
98 result
99 }
100
101 fn try_index_step(
107 expr: ExprId,
108 base_expr: ExprId,
109 index_expr: ExprId,
110 autoderef: &mut InferenceContextAutoderef<'_, 'db>,
111 index_ty: Ty<'db>,
112 ) -> Option<(Ty<'db>, Ty<'db>)> {
113 let ty = autoderef.final_ty();
114 let adjusted_ty = autoderef.ctx().structurally_resolve_type(base_expr.into(), ty);
115 debug!(
116 "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
117 index_ty={:?})",
118 expr, base_expr, adjusted_ty, index_ty
119 );
120
121 for unsize in [false, true] {
122 let mut self_ty = adjusted_ty;
123 if unsize {
124 if let TyKind::Array(element_ty, ct) = adjusted_ty.kind() {
126 let ctx = autoderef.ctx();
127 ctx.table.register_predicate(Obligation::new(
128 ctx.interner(),
129 ObligationCause::new(base_expr),
130 ctx.table.param_env,
131 ClauseKind::ConstArgHasType(ct, ctx.types.types.usize),
132 ));
133 self_ty = Ty::new_slice(ctx.interner(), element_ty);
134 } else {
135 continue;
136 }
137 }
138
139 let input_ty = autoderef.ctx().table.next_ty_var(index_expr.into());
143 let method = autoderef.ctx().try_overloaded_place_op(
144 expr,
145 self_ty,
146 Some(input_ty),
147 PlaceOp::Index,
148 );
149
150 if let Some(result) = method {
151 debug!("try_index_step: success, using overloaded indexing");
152 let method = autoderef.ctx().table.register_infer_ok(result);
153
154 let infer_ok = autoderef.adjust_steps_as_infer_ok();
155 let mut adjustments = autoderef.ctx().table.register_infer_ok(infer_ok);
156 if let TyKind::Ref(region, _, Mutability::Not) =
157 method.sig.inputs_and_output.inputs()[0].kind()
158 {
159 adjustments.push(Adjustment {
160 kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)),
161 target: Ty::new_imm_ref(autoderef.ctx().interner(), region, adjusted_ty)
162 .store(),
163 });
164 } else {
165 panic!("input to index is not a ref?");
166 }
167 if unsize {
168 adjustments.push(Adjustment {
169 kind: Adjust::Pointer(PointerCast::Unsize),
170 target: method.sig.inputs_and_output.inputs()[0].store(),
171 });
172 }
173 autoderef.ctx().write_expr_adj(base_expr, adjustments.into_boxed_slice());
174
175 autoderef.ctx().write_method_resolution(expr, method.def_id, method.args);
176
177 return Some((input_ty, autoderef.ctx().make_overloaded_place_return_type(method)));
178 }
179 }
180
181 None
182 }
183
184 pub(super) fn try_overloaded_place_op(
189 &self,
190 expr: ExprId,
191 base_ty: Ty<'db>,
192 opt_rhs_ty: Option<Ty<'db>>,
193 op: PlaceOp,
194 ) -> Option<InferOk<'db, MethodCallee<'db>>> {
195 debug!("try_overloaded_place_op({:?},{:?})", base_ty, op);
196
197 let (Some(imm_tr), Some(imm_op)) = (match op {
198 PlaceOp::Deref => (self.lang_items.Deref, self.lang_items.Deref_deref),
199 PlaceOp::Index => (self.lang_items.Index, self.lang_items.Index_index),
200 }) else {
201 return None;
203 };
204
205 let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
208 self.table.lookup_method_for_operator(
209 ObligationCause::new(expr),
210 imm_tr,
211 imm_op,
212 base_ty,
213 opt_rhs_ty,
214 treat_opaques,
215 )
216 }
217
218 pub(super) fn try_mutable_overloaded_place_op(
219 table: &InferenceTable<'db>,
220 expr: ExprId,
221 base_ty: Ty<'db>,
222 opt_rhs_ty: Option<Ty<'db>>,
223 op: PlaceOp,
224 ) -> Option<InferOk<'db, MethodCallee<'db>>> {
225 debug!("try_mutable_overloaded_place_op({:?},{:?})", base_ty, op);
226
227 let lang_items = table.interner().lang_items();
228 let (Some(mut_tr), Some(mut_op)) = (match op {
229 PlaceOp::Deref => (lang_items.DerefMut, lang_items.DerefMut_deref_mut),
230 PlaceOp::Index => (lang_items.IndexMut, lang_items.IndexMut_index_mut),
231 }) else {
232 return None;
234 };
235
236 let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
241 table.lookup_method_for_operator(
242 ObligationCause::new(expr),
243 mut_tr,
244 mut_op,
245 base_ty,
246 opt_rhs_ty,
247 treat_opaques,
248 )
249 }
250
251 pub(super) fn convert_place_op_to_mutable(
252 &mut self,
253 op: PlaceOp,
254 expr: ExprId,
255 base_expr: ExprId,
256 index_expr: Option<ExprId>,
257 ) {
258 debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
259 if !self.result.method_resolutions.contains_key(&expr) {
260 debug!("convert_place_op_to_mutable - builtin, nothing to do");
261 return;
262 }
263
264 let base_ty = self
266 .expr_ty_after_adjustments(base_expr)
267 .builtin_deref(false)
268 .expect("place op takes something that is not a ref");
269
270 let arg_ty = match op {
271 PlaceOp::Deref => None,
272 PlaceOp::Index => {
273 Some(self.expr_ty_after_adjustments(
284 index_expr.expect("`PlaceOp::Index` should have `index_expr`"),
285 ))
286 }
287 };
288 let method = Self::try_mutable_overloaded_place_op(&self.table, expr, base_ty, arg_ty, op);
289 let method = match method {
290 Some(ok) => self.table.register_infer_ok(ok),
291 None => return,
294 };
295 debug!("convert_place_op_to_mutable: method={:?}", method);
296 self.result.method_resolutions.insert(expr, (method.def_id, method.args.store()));
297
298 let TyKind::Ref(region, _, Mutability::Mut) =
299 method.sig.inputs_and_output.inputs()[0].kind()
300 else {
301 panic!("input to mutable place op is not a mut ref?");
302 };
303
304 let base_expr_ty = self.expr_ty(base_expr);
307 let interner = self.interner();
308 if let Some(adjustments) = self.result.expr_adjustments.get_mut(&base_expr) {
309 let mut source = base_expr_ty;
310 for adjustment in &mut adjustments[..] {
311 if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
312 debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
313 let mutbl = AutoBorrowMutability::Mut {
314 allow_two_phase_borrow: AllowTwoPhase::No,
319 };
320 adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(mutbl));
321 adjustment.target = Ty::new_ref(interner, region, source, mutbl.into()).store();
322 }
323 source = adjustment.target.as_ref();
324 }
325
326 if let [
328 ..,
329 Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
330 Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target },
331 ] = adjustments[..]
332 {
333 *target = method.sig.inputs_and_output.inputs()[0].store();
334 }
335 }
336 }
337}