1use hir_def::FunctionId;
4use rustc_type_ir::inherent::Region as _;
5
6use super::*;
7use crate::{
8 mir::{MutBorrowKind, Operand, OperandKind},
9 next_solver::Region,
10};
11
12impl<'db> MirLowerCtx<'_, 'db> {
13 fn lower_expr_to_some_place_without_adjust(
14 &mut self,
15 expr_id: ExprId,
16 prev_block: BasicBlockId,
17 ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> {
18 let ty = self.expr_ty_without_adjust(expr_id);
19 let place = self.temp(ty, prev_block, expr_id.into())?.into();
20 let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)?
21 else {
22 return Ok(None);
23 };
24 Ok(Some((place, current)))
25 }
26
27 fn lower_expr_to_some_place_with_adjust(
28 &mut self,
29 expr_id: ExprId,
30 prev_block: BasicBlockId,
31 adjustments: &[Adjustment],
32 ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> {
33 let ty = adjustments
34 .last()
35 .map(|it| it.target.as_ref())
36 .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
37 let place = self.temp(ty, prev_block, expr_id.into())?.into();
38 let Some(current) =
39 self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments)?
40 else {
41 return Ok(None);
42 };
43 Ok(Some((place, current)))
44 }
45
46 pub(super) fn lower_expr_as_place_with_adjust(
47 &mut self,
48 current: BasicBlockId,
49 expr_id: ExprId,
50 upgrade_rvalue: bool,
51 adjustments: &[Adjustment],
52 ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> {
53 let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| {
54 if !upgrade_rvalue {
55 return Err(MirLowerError::MutatingRvalue);
56 }
57 this.lower_expr_to_some_place_with_adjust(expr_id, current, adjustments)
58 };
59 if let Some((last, rest)) = adjustments.split_last() {
60 match last.kind {
61 Adjust::Deref(None) => {
62 let Some(mut it) = self.lower_expr_as_place_with_adjust(
63 current,
64 expr_id,
65 upgrade_rvalue,
66 rest,
67 )?
68 else {
69 return Ok(None);
70 };
71 it.0 = it.0.project(ProjectionElem::Deref);
72 Ok(Some(it))
73 }
74 Adjust::Deref(Some(od)) => {
75 let Some((r, current)) = self.lower_expr_as_place_with_adjust(
76 current,
77 expr_id,
78 upgrade_rvalue,
79 rest,
80 )?
81 else {
82 return Ok(None);
83 };
84 self.lower_overloaded_deref(
85 current,
86 r,
87 rest.last()
88 .map(|it| it.target.as_ref())
89 .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)),
90 last.target.as_ref(),
91 expr_id.into(),
92 match od.0 {
93 Mutability::Mut => true,
94 Mutability::Not => false,
95 },
96 )
97 }
98 Adjust::NeverToAny | Adjust::Borrow(_) | Adjust::Pointer(_) => try_rvalue(self),
99 }
100 } else {
101 self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue)
102 }
103 }
104
105 pub(super) fn lower_expr_as_place(
106 &mut self,
107 current: BasicBlockId,
108 expr_id: ExprId,
109 upgrade_rvalue: bool,
110 ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> {
111 match self.infer.expr_adjustments.get(&expr_id) {
112 Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a),
113 None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue),
114 }
115 }
116
117 pub(super) fn lower_expr_as_place_without_adjust(
118 &mut self,
119 current: BasicBlockId,
120 expr_id: ExprId,
121 upgrade_rvalue: bool,
122 ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> {
123 let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| {
124 if !upgrade_rvalue {
125 return Err(MirLowerError::MutatingRvalue);
126 }
127 this.lower_expr_to_some_place_without_adjust(expr_id, current)
128 };
129 match &self.store[expr_id] {
130 Expr::Path(p) => {
131 let resolver_guard =
132 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
133 let hygiene = self.store.expr_path_hygiene(expr_id);
134 let resolved = self.resolver.resolve_path_in_value_ns_fully(self.db, p, hygiene);
135 self.resolver.reset_to_guard(resolver_guard);
136 let Some(pr) = resolved else {
137 return try_rvalue(self);
138 };
139 match pr {
140 ValueNs::LocalBinding(pat_id) => {
141 Ok(Some((self.binding_local(pat_id)?.into(), current)))
142 }
143 ValueNs::StaticId(s) => {
144 let ty = self.expr_ty_without_adjust(expr_id);
145 let ref_ty = Ty::new_ref(
146 self.interner(),
147 Region::new_static(self.interner()),
148 ty,
149 Mutability::Not,
150 );
151 let temp = self.temp(ref_ty, current, expr_id.into())?.into();
152 self.push_assignment(
153 current,
154 temp,
155 Operand { kind: OperandKind::Static(s), span: None }.into(),
156 expr_id.into(),
157 );
158 Ok(Some((temp.project(ProjectionElem::Deref), current)))
159 }
160 _ => try_rvalue(self),
161 }
162 }
163 Expr::UnaryOp { expr, op: hir_def::hir::UnaryOp::Deref } => {
164 let is_builtin = match self.expr_ty_without_adjust(*expr).kind() {
165 TyKind::Ref(..) | TyKind::RawPtr(..) => true,
166 TyKind::Adt(id, _) => id.is_box(),
167 _ => false,
168 };
169 if !is_builtin {
170 let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
171 return Ok(None);
172 };
173 return self.lower_overloaded_deref(
174 current,
175 p,
176 self.expr_ty_without_adjust(*expr),
177 self.expr_ty_without_adjust(expr_id),
178 expr_id.into(),
179 'b: {
180 if let Some((f, _)) = self.infer.method_resolution(expr_id)
181 && let Some(deref_fn) = self.lang_items().DerefMut_deref_mut
182 {
183 break 'b deref_fn == f;
184 }
185 false
186 },
187 );
188 }
189 let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
190 return Ok(None);
191 };
192 r = r.project(ProjectionElem::Deref);
193 Ok(Some((r, current)))
194 }
195 Expr::UnaryOp { .. } => try_rvalue(self),
196 Expr::Field { expr, .. } => {
197 let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
198 return Ok(None);
199 };
200 self.push_field_projection(&mut r, expr_id)?;
201 Ok(Some((r, current)))
202 }
203 Expr::Index { base, index } => {
204 let base_ty = self.expr_ty_after_adjustments(*base);
205 let index_ty = self.expr_ty_after_adjustments(*index);
206 if !matches!(index_ty.kind(), TyKind::Uint(rustc_ast_ir::UintTy::Usize))
207 || !matches!(
208 base_ty.strip_reference().kind(),
209 TyKind::Array(..) | TyKind::Slice(..)
210 )
211 {
212 let Some(index_fn) = self.infer.method_resolution(expr_id) else {
213 return Err(MirLowerError::UnresolvedMethod(
214 "[overloaded index]".to_owned(),
215 ));
216 };
217 let Some((base_place, current)) =
218 self.lower_expr_as_place(current, *base, true)?
219 else {
220 return Ok(None);
221 };
222 let Some((index_operand, current)) =
223 self.lower_expr_to_some_operand(*index, current)?
224 else {
225 return Ok(None);
226 };
227 return self.lower_overloaded_index(
228 current,
229 base_place,
230 base_ty,
231 self.expr_ty_without_adjust(expr_id),
232 index_operand,
233 expr_id.into(),
234 index_fn,
235 );
236 }
237 let adjusts = self
238 .infer
239 .expr_adjustments
240 .get(base)
241 .and_then(|it| it.split_last())
242 .map(|it| it.1)
243 .unwrap_or(&[]);
244 let Some((mut p_base, current)) =
245 self.lower_expr_as_place_with_adjust(current, *base, true, adjusts)?
246 else {
247 return Ok(None);
248 };
249 let l_index =
250 self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?;
251 let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)?
252 else {
253 return Ok(None);
254 };
255 p_base = p_base.project(ProjectionElem::Index(l_index));
256 Ok(Some((p_base, current)))
257 }
258 _ => try_rvalue(self),
259 }
260 }
261
262 fn lower_overloaded_index(
263 &mut self,
264 current: BasicBlockId,
265 place: PlaceRef<'db>,
266 base_ty: Ty<'db>,
267 result_ty: Ty<'db>,
268 index_operand: Operand,
269 span: MirSpan,
270 index_fn: (FunctionId, GenericArgs<'db>),
271 ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> {
272 let mutability = match base_ty.as_reference() {
273 Some((_, _, mutability)) => mutability,
274 None => Mutability::Not,
275 };
276 let result_ref =
277 Ty::new_ref(self.interner(), Region::error(self.interner()), result_ty, mutability);
278 let mut result = self.temp(result_ref, current, span)?.into();
279 let index_fn_op = Operand::const_zst(Ty::new_fn_def(
280 self.interner(),
281 CallableDefId::FunctionId(index_fn.0).into(),
282 index_fn.1,
283 ));
284 let Some(current) = self.lower_call(
285 index_fn_op,
286 Box::new([
287 Operand { kind: OperandKind::Copy(place.store()), span: None },
288 index_operand,
289 ]),
290 result,
291 current,
292 false,
293 span,
294 )?
295 else {
296 return Ok(None);
297 };
298 result = result.project(ProjectionElem::Deref);
299 Ok(Some((result, current)))
300 }
301
302 fn lower_overloaded_deref(
303 &mut self,
304 current: BasicBlockId,
305 place: PlaceRef<'db>,
306 source_ty: Ty<'db>,
307 target_ty: Ty<'db>,
308 span: MirSpan,
309 mutability: bool,
310 ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> {
311 let lang_items = self.lang_items();
312 let (mutability, deref_fn, borrow_kind) = if !mutability {
313 (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared)
314 } else {
315 (
316 Mutability::Mut,
317 lang_items.DerefMut_deref_mut,
318 BorrowKind::Mut { kind: MutBorrowKind::Default },
319 )
320 };
321 let error_region = Region::error(self.interner());
322 let ty_ref = Ty::new_ref(self.interner(), error_region, source_ty, mutability);
323 let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability);
324 let ref_place = self.temp(ty_ref, current, span)?.into();
325 self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place.store()), span);
326 let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?;
327 let deref_fn_op = Operand::const_zst(Ty::new_fn_def(
328 self.interner(),
329 CallableDefId::FunctionId(deref_fn).into(),
330 GenericArgs::new_from_slice(&[source_ty.into()]),
331 ));
332 let mut result = self.temp(target_ty_ref, current, span)?.into();
333 let Some(current) = self.lower_call(
334 deref_fn_op,
335 Box::new([Operand { kind: OperandKind::Copy(ref_place.store()), span: None }]),
336 result,
337 current,
338 false,
339 span,
340 )?
341 else {
342 return Ok(None);
343 };
344 result = result.project(ProjectionElem::Deref);
345 Ok(Some((result, current)))
346 }
347}