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.rs | 351 |
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) +} |