Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/lower/as_place.rs')
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs236
1 files changed, 236 insertions, 0 deletions
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
new file mode 100644
index 0000000000..ada52c61ec
--- /dev/null
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -0,0 +1,236 @@
+//! MIR lowering for places
+
+use super::*;
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirLowerError::NotSupported(format!($x)))
+ };
+}
+
+impl MirLowerCtx<'_> {
+ fn lower_expr_to_some_place_without_adjust(
+ &mut self,
+ expr_id: ExprId,
+ prev_block: BasicBlockId,
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let ty = self.expr_ty(expr_id);
+ let place = self.temp(ty)?;
+ let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
+ return Ok(None);
+ };
+ Ok(Some((place.into(), current)))
+ }
+
+ fn lower_expr_to_some_place_with_adjust(
+ &mut self,
+ expr_id: ExprId,
+ prev_block: BasicBlockId,
+ adjustments: &[Adjustment],
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let ty =
+ adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
+ let place = self.temp(ty)?;
+ let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
+ return Ok(None);
+ };
+ Ok(Some((place.into(), current)))
+ }
+
+ pub(super) fn lower_expr_as_place_with_adjust(
+ &mut self,
+ current: BasicBlockId,
+ expr_id: ExprId,
+ upgrade_rvalue: bool,
+ adjustments: &[Adjustment],
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let try_rvalue = |this: &mut MirLowerCtx<'_>| {
+ if !upgrade_rvalue {
+ return Err(MirLowerError::MutatingRvalue);
+ }
+ this.lower_expr_to_some_place_with_adjust(expr_id, current, adjustments)
+ };
+ if let Some((last, rest)) = adjustments.split_last() {
+ match last.kind {
+ Adjust::Deref(None) => {
+ let Some(mut x) = self.lower_expr_as_place_with_adjust(
+ current,
+ expr_id,
+ upgrade_rvalue,
+ rest,
+ )? else {
+ return Ok(None);
+ };
+ x.0.projection.push(ProjectionElem::Deref);
+ Ok(Some(x))
+ }
+ Adjust::Deref(Some(od)) => {
+ let Some((r, current)) = self.lower_expr_as_place_with_adjust(
+ current,
+ expr_id,
+ upgrade_rvalue,
+ rest,
+ )? else {
+ return Ok(None);
+ };
+ self.lower_overloaded_deref(
+ current,
+ r,
+ rest.last()
+ .map(|x| x.target.clone())
+ .unwrap_or_else(|| self.expr_ty(expr_id)),
+ last.target.clone(),
+ expr_id.into(),
+ match od.0 {
+ Some(Mutability::Mut) => true,
+ Some(Mutability::Not) => false,
+ None => {
+ not_supported!("implicit overloaded deref with unknown mutability")
+ }
+ },
+ )
+ }
+ Adjust::NeverToAny | Adjust::Borrow(_) | Adjust::Pointer(_) => try_rvalue(self),
+ }
+ } else {
+ self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue)
+ }
+ }
+
+ pub(super) fn lower_expr_as_place(
+ &mut self,
+ current: BasicBlockId,
+ expr_id: ExprId,
+ upgrade_rvalue: bool,
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ match self.infer.expr_adjustments.get(&expr_id) {
+ Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a),
+ None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue),
+ }
+ }
+
+ fn lower_expr_as_place_without_adjust(
+ &mut self,
+ current: BasicBlockId,
+ expr_id: ExprId,
+ upgrade_rvalue: bool,
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let try_rvalue = |this: &mut MirLowerCtx<'_>| {
+ if !upgrade_rvalue {
+ return Err(MirLowerError::MutatingRvalue);
+ }
+ this.lower_expr_to_some_place_without_adjust(expr_id, current)
+ };
+ match &self.body.exprs[expr_id] {
+ Expr::Path(p) => {
+ let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+ let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
+ return Err(MirLowerError::unresolved_path(self.db, p));
+ };
+ let pr = match pr {
+ ResolveValueResult::ValueNs(v) => v,
+ ResolveValueResult::Partial(..) => return try_rvalue(self),
+ };
+ match pr {
+ ValueNs::LocalBinding(pat_id) => {
+ Ok(Some((self.result.binding_locals[pat_id].into(), current)))
+ }
+ _ => try_rvalue(self),
+ }
+ }
+ Expr::UnaryOp { expr, op } => match op {
+ hir_def::expr::UnaryOp::Deref => {
+ if !matches!(
+ self.expr_ty(*expr).kind(Interner),
+ TyKind::Ref(..) | TyKind::Raw(..)
+ ) {
+ let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
+ return Ok(None);
+ };
+ not_supported!("explicit overloaded deref");
+ }
+ let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+ return Ok(None);
+ };
+ r.projection.push(ProjectionElem::Deref);
+ Ok(Some((r, current)))
+ }
+ _ => try_rvalue(self),
+ },
+ Expr::Field { expr, .. } => {
+ let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+ return Ok(None);
+ };
+ self.push_field_projection(&mut r, expr_id)?;
+ Ok(Some((r, current)))
+ }
+ Expr::Index { base, index } => {
+ let base_ty = self.expr_ty_after_adjustments(*base);
+ let index_ty = self.expr_ty_after_adjustments(*index);
+ if index_ty != TyBuilder::usize()
+ || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
+ {
+ not_supported!("overloaded index");
+ }
+ let Some((mut p_base, current)) =
+ self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None);
+ };
+ let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
+ let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
+ return Ok(None);
+ };
+ p_base.projection.push(ProjectionElem::Index(l_index));
+ Ok(Some((p_base, current)))
+ }
+ _ => try_rvalue(self),
+ }
+ }
+
+ fn lower_overloaded_deref(
+ &mut self,
+ current: BasicBlockId,
+ place: Place,
+ source_ty: Ty,
+ target_ty: Ty,
+ span: MirSpan,
+ mutability: bool,
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
+ (Mutability::Not, LangItem::Deref, name![deref], BorrowKind::Shared)
+ } else {
+ (
+ Mutability::Mut,
+ LangItem::DerefMut,
+ name![deref_mut],
+ BorrowKind::Mut { allow_two_phase_borrow: false },
+ )
+ };
+ let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
+ let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
+ let ref_place: Place = self.temp(ty_ref)?.into();
+ self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
+ let deref_trait = self
+ .resolve_lang_item(trait_lang_item)?
+ .as_trait()
+ .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
+ let deref_fn = self
+ .db
+ .trait_data(deref_trait)
+ .method_by_name(&trait_method_name)
+ .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
+ let deref_fn_op = Operand::const_zst(
+ TyKind::FnDef(
+ self.db.intern_callable_def(CallableDefId::FunctionId(deref_fn)).into(),
+ Substitution::from1(Interner, source_ty),
+ )
+ .intern(Interner),
+ );
+ let mut result: Place = self.temp(target_ty_ref)?.into();
+ let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
+ return Ok(None);
+ };
+ result.projection.push(ProjectionElem::Deref);
+ Ok(Some((result, current)))
+ }
+}