]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-ty / src / mir / lower / as_place.rs
CommitLineData
353b0b11
FG
1//! MIR lowering for places
2
3use super::*;
4use hir_expand::name;
5
6macro_rules! not_supported {
7 ($x: expr) => {
8 return Err(MirLowerError::NotSupported(format!($x)))
9 };
10}
11
12impl MirLowerCtx<'_> {
13 fn lower_expr_to_some_place_without_adjust(
14 &mut self,
15 expr_id: ExprId,
16 prev_block: BasicBlockId,
17 ) -> Result<Option<(Place, BasicBlockId)>> {
18 let ty = self.expr_ty(expr_id);
19 let place = self.temp(ty)?;
20 let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
21 return Ok(None);
22 };
23 Ok(Some((place.into(), current)))
24 }
25
26 fn lower_expr_to_some_place_with_adjust(
27 &mut self,
28 expr_id: ExprId,
29 prev_block: BasicBlockId,
30 adjustments: &[Adjustment],
31 ) -> Result<Option<(Place, BasicBlockId)>> {
32 let ty =
33 adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
34 let place = self.temp(ty)?;
35 let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
36 return Ok(None);
37 };
38 Ok(Some((place.into(), current)))
39 }
40
41 pub(super) fn lower_expr_as_place_with_adjust(
42 &mut self,
43 current: BasicBlockId,
44 expr_id: ExprId,
45 upgrade_rvalue: bool,
46 adjustments: &[Adjustment],
47 ) -> Result<Option<(Place, BasicBlockId)>> {
48 let try_rvalue = |this: &mut MirLowerCtx<'_>| {
49 if !upgrade_rvalue {
50 return Err(MirLowerError::MutatingRvalue);
51 }
52 this.lower_expr_to_some_place_with_adjust(expr_id, current, adjustments)
53 };
54 if let Some((last, rest)) = adjustments.split_last() {
55 match last.kind {
56 Adjust::Deref(None) => {
57 let Some(mut x) = self.lower_expr_as_place_with_adjust(
58 current,
59 expr_id,
60 upgrade_rvalue,
61 rest,
62 )? else {
63 return Ok(None);
64 };
65 x.0.projection.push(ProjectionElem::Deref);
66 Ok(Some(x))
67 }
68 Adjust::Deref(Some(od)) => {
69 let Some((r, current)) = self.lower_expr_as_place_with_adjust(
70 current,
71 expr_id,
72 upgrade_rvalue,
73 rest,
74 )? else {
75 return Ok(None);
76 };
77 self.lower_overloaded_deref(
78 current,
79 r,
80 rest.last()
81 .map(|x| x.target.clone())
82 .unwrap_or_else(|| self.expr_ty(expr_id)),
83 last.target.clone(),
84 expr_id.into(),
85 match od.0 {
86 Some(Mutability::Mut) => true,
87 Some(Mutability::Not) => false,
88 None => {
89 not_supported!("implicit overloaded deref with unknown mutability")
90 }
91 },
92 )
93 }
94 Adjust::NeverToAny | Adjust::Borrow(_) | Adjust::Pointer(_) => try_rvalue(self),
95 }
96 } else {
97 self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue)
98 }
99 }
100
101 pub(super) fn lower_expr_as_place(
102 &mut self,
103 current: BasicBlockId,
104 expr_id: ExprId,
105 upgrade_rvalue: bool,
106 ) -> Result<Option<(Place, BasicBlockId)>> {
107 match self.infer.expr_adjustments.get(&expr_id) {
108 Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a),
109 None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue),
110 }
111 }
112
113 pub(super) fn lower_expr_as_place_without_adjust(
114 &mut self,
115 current: BasicBlockId,
116 expr_id: ExprId,
117 upgrade_rvalue: bool,
118 ) -> Result<Option<(Place, BasicBlockId)>> {
119 let try_rvalue = |this: &mut MirLowerCtx<'_>| {
120 if !upgrade_rvalue {
121 return Err(MirLowerError::MutatingRvalue);
122 }
123 this.lower_expr_to_some_place_without_adjust(expr_id, current)
124 };
125 match &self.body.exprs[expr_id] {
126 Expr::Path(p) => {
127 let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
128 let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
129 return Err(MirLowerError::unresolved_path(self.db, p));
130 };
131 let pr = match pr {
132 ResolveValueResult::ValueNs(v) => v,
133 ResolveValueResult::Partial(..) => return try_rvalue(self),
134 };
135 match pr {
136 ValueNs::LocalBinding(pat_id) => {
137 Ok(Some((self.result.binding_locals[pat_id].into(), current)))
138 }
139 _ => try_rvalue(self),
140 }
141 }
142 Expr::UnaryOp { expr, op } => match op {
143 hir_def::expr::UnaryOp::Deref => {
144 if !matches!(
145 self.expr_ty(*expr).kind(Interner),
146 TyKind::Ref(..) | TyKind::Raw(..)
147 ) {
148 let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
149 return Ok(None);
150 };
151 not_supported!("explicit overloaded deref");
152 }
153 let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
154 return Ok(None);
155 };
156 r.projection.push(ProjectionElem::Deref);
157 Ok(Some((r, current)))
158 }
159 _ => try_rvalue(self),
160 },
161 Expr::Field { expr, .. } => {
162 let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
163 return Ok(None);
164 };
165 self.push_field_projection(&mut r, expr_id)?;
166 Ok(Some((r, current)))
167 }
168 Expr::Index { base, index } => {
169 let base_ty = self.expr_ty_after_adjustments(*base);
170 let index_ty = self.expr_ty_after_adjustments(*index);
171 if index_ty != TyBuilder::usize()
172 || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
173 {
174 not_supported!("overloaded index");
175 }
176 let Some((mut p_base, current)) =
177 self.lower_expr_as_place(current, *base, true)? else {
178 return Ok(None);
179 };
180 let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
181 let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
182 return Ok(None);
183 };
184 p_base.projection.push(ProjectionElem::Index(l_index));
185 Ok(Some((p_base, current)))
186 }
187 _ => try_rvalue(self),
188 }
189 }
190
191 fn lower_overloaded_deref(
192 &mut self,
193 current: BasicBlockId,
194 place: Place,
195 source_ty: Ty,
196 target_ty: Ty,
197 span: MirSpan,
198 mutability: bool,
199 ) -> Result<Option<(Place, BasicBlockId)>> {
200 let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
201 (Mutability::Not, LangItem::Deref, name![deref], BorrowKind::Shared)
202 } else {
203 (
204 Mutability::Mut,
205 LangItem::DerefMut,
206 name![deref_mut],
207 BorrowKind::Mut { allow_two_phase_borrow: false },
208 )
209 };
210 let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
211 let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
212 let ref_place: Place = self.temp(ty_ref)?.into();
213 self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
214 let deref_trait = self
215 .resolve_lang_item(trait_lang_item)?
216 .as_trait()
217 .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
218 let deref_fn = self
219 .db
220 .trait_data(deref_trait)
221 .method_by_name(&trait_method_name)
222 .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
223 let deref_fn_op = Operand::const_zst(
224 TyKind::FnDef(
225 self.db.intern_callable_def(CallableDefId::FunctionId(deref_fn)).into(),
226 Substitution::from1(Interner, source_ty),
227 )
228 .intern(Interner),
229 );
230 let mut result: Place = self.temp(target_ty_ref)?.into();
231 let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
232 return Ok(None);
233 };
234 result.projection.push(ProjectionElem::Deref);
235 Ok(Some((result, current)))
236 }
237}