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.rs91
1 files changed, 87 insertions, 4 deletions
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index fe8147dcd3..425904850b 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -1,6 +1,7 @@
//! MIR lowering for places
use super::*;
+use hir_def::FunctionId;
use hir_expand::name;
macro_rules! not_supported {
@@ -125,7 +126,7 @@ impl MirLowerCtx<'_> {
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 {
+ let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) else {
return Err(MirLowerError::unresolved_path(self.db, p));
};
let pr = match pr {
@@ -145,10 +146,32 @@ impl MirLowerCtx<'_> {
self.expr_ty(*expr).kind(Interner),
TyKind::Ref(..) | TyKind::Raw(..)
) {
- let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
+ let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- not_supported!("explicit overloaded deref");
+ return self.lower_overloaded_deref(
+ current,
+ p,
+ self.expr_ty_after_adjustments(*expr),
+ self.expr_ty(expr_id),
+ expr_id.into(),
+ 'b: {
+ if let Some((f, _)) = self.infer.method_resolution(expr_id) {
+ if let Some(deref_trait) =
+ self.resolve_lang_item(LangItem::DerefMut)?.as_trait()
+ {
+ if let Some(deref_fn) = self
+ .db
+ .trait_data(deref_trait)
+ .method_by_name(&name![deref_mut])
+ {
+ break 'b deref_fn == f;
+ }
+ }
+ }
+ false
+ },
+ );
}
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
@@ -171,7 +194,24 @@ impl MirLowerCtx<'_> {
if index_ty != TyBuilder::usize()
|| !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
{
- not_supported!("overloaded index");
+ let Some(index_fn) = self.infer.method_resolution(expr_id) else {
+ return Err(MirLowerError::UnresolvedMethod);
+ };
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None);
+ };
+ let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else {
+ return Ok(None);
+ };
+ return self.lower_overloaded_index(
+ current,
+ base_place,
+ self.expr_ty_after_adjustments(*base),
+ self.expr_ty(expr_id),
+ index_operand,
+ expr_id.into(),
+ index_fn,
+ );
}
let Some((mut p_base, current)) =
self.lower_expr_as_place(current, *base, true)? else {
@@ -188,6 +228,49 @@ impl MirLowerCtx<'_> {
}
}
+ fn lower_overloaded_index(
+ &mut self,
+ current: BasicBlockId,
+ place: Place,
+ base_ty: Ty,
+ result_ty: Ty,
+ index_operand: Operand,
+ span: MirSpan,
+ index_fn: (FunctionId, Substitution),
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let is_mutable = 'b: {
+ if let Some(index_mut_trait) = self.resolve_lang_item(LangItem::IndexMut)?.as_trait() {
+ if let Some(index_mut_fn) =
+ self.db.trait_data(index_mut_trait).method_by_name(&name![index_mut])
+ {
+ break 'b index_mut_fn == index_fn.0;
+ }
+ }
+ false
+ };
+ let (mutability, borrow_kind) = match is_mutable {
+ true => (Mutability::Mut, BorrowKind::Mut { allow_two_phase_borrow: false }),
+ false => (Mutability::Not, BorrowKind::Shared),
+ };
+ let base_ref = TyKind::Ref(mutability, static_lifetime(), base_ty).intern(Interner);
+ let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner);
+ let ref_place: Place = self.temp(base_ref)?.into();
+ self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
+ let mut result: Place = self.temp(result_ref)?.into();
+ let index_fn_op = Operand::const_zst(
+ TyKind::FnDef(
+ self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(),
+ index_fn.1,
+ )
+ .intern(Interner),
+ );
+ let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false)? else {
+ return Ok(None);
+ };
+ result.projection.push(ProjectionElem::Deref);
+ Ok(Some((result, current)))
+ }
+
fn lower_overloaded_deref(
&mut self,
current: BasicBlockId,