| //! 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 base_db::salsa::Cycle; |
| use chalk_ir::{ |
| fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, |
| ConstData, DebruijnIndex, |
| }; |
| use hir_def::DefWithBodyId; |
| use triomphe::Arc; |
| |
| use crate::{ |
| consteval::{intern_const_scalar, unknown_const}, |
| db::HirDatabase, |
| from_placeholder_idx, |
| infer::normalize, |
| utils::{generics, Generics}, |
| ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind, |
| }; |
| |
| use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind}; |
| |
| macro_rules! not_supported { |
| ($it: expr) => { |
| return Err(MirLowerError::NotSupported(format!($it))) |
| }; |
| } |
| |
| 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 it = from_placeholder_idx(self.db, idx); |
| let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { |
| not_supported!("missing idx in generics"); |
| }; |
| Ok(self |
| .subst |
| .as_slice(Interner) |
| .get(idx) |
| .and_then(|it| it.constant(Interner)) |
| .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, 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 it = from_placeholder_idx(self.db, idx); |
| let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { |
| not_supported!("missing idx in generics"); |
| }; |
| Ok(self |
| .subst |
| .as_slice(Interner) |
| .get(idx) |
| .and_then(|it| it.ty(Interner)) |
| .ok_or_else(|| MirLowerError::GenericArgNotProvided(it, 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(|it| it.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 subst = subst.clone(); |
| self.fill_subst(&mut subst)?; |
| *c = intern_const_scalar( |
| crate::ConstScalar::UnevaluatedConst(*const_id, subst), |
| c.data(Interner).ty.clone(), |
| ); |
| } |
| 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::FakeRead(_) |
| | 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::UnwindResume |
| | TerminatorKind::Abort |
| | TerminatorKind::Return |
| | TerminatorKind::Unreachable |
| | TerminatorKind::Drop { .. } |
| | TerminatorKind::DropAndReplace { .. } |
| | TerminatorKind::Assert { .. } |
| | TerminatorKind::Yield { .. } |
| | TerminatorKind::CoroutineDrop |
| | 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, |
| _: &Cycle, |
| _: &DefWithBodyId, |
| _: &Substitution, |
| _: &Arc<crate::TraitEnvironment>, |
| ) -> Result<Arc<MirBody>, MirLowerError> { |
| 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) |
| } |