Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/monomorphization.rs')
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs351
1 files changed, 351 insertions, 0 deletions
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
new file mode 100644
index 0000000000..ce3f7a8e51
--- /dev/null
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -0,0 +1,351 @@
+//! Monomorphization of mir, which is used in mir interpreter and const eval.
+//!
+//! The job of monomorphization is:
+//! * Monomorphization. That is, replacing `Option<T>` with `Option<i32>` where `T:=i32` substitution
+//! is provided
+//! * Normalizing types, for example replacing RPIT of other functions called in this body.
+//!
+//! So the monomorphization should be called even if the substitution is empty.
+
+use std::mem;
+
+use chalk_ir::{
+ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
+ ConstData, DebruijnIndex,
+};
+use hir_def::{DefWithBodyId, GeneralConstId};
+use triomphe::Arc;
+
+use crate::{
+ consteval::unknown_const,
+ db::HirDatabase,
+ from_placeholder_idx,
+ infer::normalize,
+ method_resolution::lookup_impl_const,
+ utils::{generics, Generics},
+ ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
+};
+
+use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind};
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirLowerError::NotSupported(format!($x)))
+ };
+}
+
+struct Filler<'a> {
+ db: &'a dyn HirDatabase,
+ trait_env: Arc<TraitEnvironment>,
+ subst: &'a Substitution,
+ generics: Option<Generics>,
+ owner: DefWithBodyId,
+}
+impl FallibleTypeFolder<Interner> for Filler<'_> {
+ type Error = MirLowerError;
+
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn try_fold_ty(
+ &mut self,
+ ty: Ty,
+ outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ match ty.kind(Interner) {
+ TyKind::AssociatedType(id, subst) => {
+ // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes
+ // this kind of associated types.
+ Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
+ associated_ty_id: *id,
+ substitution: subst.clone().try_fold_with(self, outer_binder)?,
+ }))
+ .intern(Interner))
+ }
+ TyKind::OpaqueType(id, subst) => {
+ let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
+ let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?;
+ match impl_trait_id {
+ crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+ let infer = self.db.infer(func.into());
+ let filler = &mut Filler {
+ db: self.db,
+ owner: self.owner,
+ trait_env: self.trait_env.clone(),
+ subst: &subst,
+ generics: Some(generics(self.db.upcast(), func.into())),
+ };
+ filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
+ }
+ crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+ not_supported!("async block impl trait");
+ }
+ }
+ }
+ _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
+ }
+ }
+
+ fn try_fold_free_placeholder_const(
+ &mut self,
+ _ty: chalk_ir::Ty<Interner>,
+ idx: chalk_ir::PlaceholderIndex,
+ _outer_binder: DebruijnIndex,
+ ) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+ not_supported!("missing idx in generics");
+ };
+ Ok(self
+ .subst
+ .as_slice(Interner)
+ .get(idx)
+ .and_then(|x| x.constant(Interner))
+ .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+ .clone())
+ }
+
+ fn try_fold_free_placeholder_ty(
+ &mut self,
+ idx: chalk_ir::PlaceholderIndex,
+ _outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+ not_supported!("missing idx in generics");
+ };
+ Ok(self
+ .subst
+ .as_slice(Interner)
+ .get(idx)
+ .and_then(|x| x.ty(Interner))
+ .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+ .clone())
+ }
+
+ fn try_fold_const(
+ &mut self,
+ constant: chalk_ir::Const<Interner>,
+ outer_binder: DebruijnIndex,
+ ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
+ let next_ty = normalize(
+ self.db,
+ self.trait_env.clone(),
+ constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?,
+ );
+ ConstData { ty: next_ty, value: constant.data(Interner).value.clone() }
+ .intern(Interner)
+ .try_super_fold_with(self, outer_binder)
+ }
+}
+
+impl Filler<'_> {
+ fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(ty, TyKind::Error.intern(Interner));
+ *ty = normalize(
+ self.db,
+ self.trait_env.clone(),
+ tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?,
+ );
+ Ok(())
+ }
+
+ fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone()));
+ *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+ Ok(())
+ }
+
+ fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(ty, Substitution::empty(Interner));
+ *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+ Ok(())
+ }
+
+ fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> {
+ match op {
+ Operand::Constant(c) => {
+ match &c.data(Interner).value {
+ chalk_ir::ConstValue::BoundVar(b) => {
+ let resolved = self
+ .subst
+ .as_slice(Interner)
+ .get(b.index)
+ .ok_or_else(|| {
+ MirLowerError::GenericArgNotProvided(
+ self.generics
+ .as_ref()
+ .and_then(|x| x.iter().nth(b.index))
+ .unwrap()
+ .0,
+ self.subst.clone(),
+ )
+ })?
+ .assert_const_ref(Interner);
+ *c = resolved.clone();
+ }
+ chalk_ir::ConstValue::InferenceVar(_)
+ | chalk_ir::ConstValue::Placeholder(_) => {}
+ chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
+ crate::ConstScalar::UnevaluatedConst(const_id, subst) => {
+ let mut const_id = *const_id;
+ let mut subst = subst.clone();
+ self.fill_subst(&mut subst)?;
+ if let GeneralConstId::ConstId(c) = const_id {
+ let (c, s) = lookup_impl_const(
+ self.db,
+ self.db.trait_environment_for_body(self.owner),
+ c,
+ subst,
+ );
+ const_id = GeneralConstId::ConstId(c);
+ subst = s;
+ }
+ let result =
+ self.db.const_eval(const_id.into(), subst).map_err(|e| {
+ let name = const_id.name(self.db.upcast());
+ MirLowerError::ConstEvalError(name, Box::new(e))
+ })?;
+ *c = result;
+ }
+ crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
+ },
+ }
+ self.fill_const(c)?;
+ }
+ Operand::Copy(_) | Operand::Move(_) | Operand::Static(_) => (),
+ }
+ Ok(())
+ }
+
+ fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError> {
+ for (_, l) in body.locals.iter_mut() {
+ self.fill_ty(&mut l.ty)?;
+ }
+ for (_, bb) in body.basic_blocks.iter_mut() {
+ for statement in &mut bb.statements {
+ match &mut statement.kind {
+ StatementKind::Assign(_, r) => match r {
+ Rvalue::Aggregate(ak, ops) => {
+ for op in &mut **ops {
+ self.fill_operand(op)?;
+ }
+ match ak {
+ super::AggregateKind::Array(ty)
+ | super::AggregateKind::Tuple(ty)
+ | super::AggregateKind::Closure(ty) => self.fill_ty(ty)?,
+ super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?,
+ super::AggregateKind::Union(_, _) => (),
+ }
+ }
+ Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => {
+ self.fill_ty(ty)?;
+ }
+ Rvalue::Use(op) => {
+ self.fill_operand(op)?;
+ }
+ Rvalue::Repeat(op, len) => {
+ self.fill_operand(op)?;
+ self.fill_const(len)?;
+ }
+ Rvalue::Ref(_, _)
+ | Rvalue::Len(_)
+ | Rvalue::Cast(_, _, _)
+ | Rvalue::CheckedBinaryOp(_, _, _)
+ | Rvalue::UnaryOp(_, _)
+ | Rvalue::Discriminant(_)
+ | Rvalue::CopyForDeref(_) => (),
+ },
+ StatementKind::Deinit(_)
+ | StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ if let Some(terminator) = &mut bb.terminator {
+ match &mut terminator.kind {
+ TerminatorKind::Call { func, args, .. } => {
+ self.fill_operand(func)?;
+ for op in &mut **args {
+ self.fill_operand(op)?;
+ }
+ }
+ TerminatorKind::SwitchInt { discr, .. } => {
+ self.fill_operand(discr)?;
+ }
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::Yield { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. } => (),
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+pub fn monomorphized_mir_body_query(
+ db: &dyn HirDatabase,
+ owner: DefWithBodyId,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ let body = db.mir_body(owner)?;
+ let mut body = (*body).clone();
+ filler.fill_body(&mut body)?;
+ Ok(Arc::new(body))
+}
+
+pub fn monomorphized_mir_body_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &DefWithBodyId,
+ _: &Substitution,
+ _: &Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ return Err(MirLowerError::Loop);
+}
+
+pub fn monomorphized_mir_body_for_closure_query(
+ db: &dyn HirDatabase,
+ closure: ClosureId,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ let (owner, _) = db.lookup_intern_closure(closure.into());
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ let body = db.mir_body_for_closure(closure)?;
+ let mut body = (*body).clone();
+ filler.fill_body(&mut body)?;
+ Ok(Arc::new(body))
+}
+
+// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query.
+pub fn monomorphize_mir_body_bad(
+ db: &dyn HirDatabase,
+ mut body: MirBody,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<MirBody, MirLowerError> {
+ let owner = body.owner;
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ filler.fill_body(&mut body)?;
+ Ok(body)
+}