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