|  | //! 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::{ | 
|  | ConstData, DebruijnIndex, | 
|  | fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, | 
|  | }; | 
|  | use hir_def::DefWithBodyId; | 
|  | use triomphe::Arc; | 
|  |  | 
|  | use crate::{ | 
|  | Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind, | 
|  | consteval::{intern_const_scalar, unknown_const}, | 
|  | db::{HirDatabase, InternedClosure, InternedClosureId}, | 
|  | from_placeholder_idx, | 
|  | generics::{Generics, generics}, | 
|  | infer::normalize, | 
|  | }; | 
|  |  | 
|  | use super::{MirBody, MirLowerError, Operand, OperandKind, 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<'a>>, | 
|  | subst: &'a Substitution, | 
|  | generics: Option<Generics>, | 
|  | } | 
|  | 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::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { | 
|  | opaque_ty_id: id, | 
|  | substitution: subst, | 
|  | })) | 
|  | | 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, | 
|  | trait_env: self.trait_env.clone(), | 
|  | subst: &subst, | 
|  | generics: Some(generics(self.db, func.into())), | 
|  | }; | 
|  | filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) | 
|  | } | 
|  | crate::ImplTraitId::TypeAliasImplTrait(..) => { | 
|  | not_supported!("type alias impl trait"); | 
|  | } | 
|  | 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).0; | 
|  | let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_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).0; | 
|  | let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_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 &mut op.kind { | 
|  | OperandKind::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)) | 
|  | .and_then(|(id, _)| match id { | 
|  | hir_def::GenericParamId::ConstParamId(id) => { | 
|  | Some(hir_def::TypeOrConstParamId::from(id)) | 
|  | } | 
|  | hir_def::GenericParamId::TypeParamId(id) => { | 
|  | Some(hir_def::TypeOrConstParamId::from(id)) | 
|  | } | 
|  | _ => None, | 
|  | }) | 
|  | .unwrap(), | 
|  | 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)?; | 
|  | } | 
|  | OperandKind::Copy(_) | OperandKind::Move(_) | OperandKind::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(_) => (), | 
|  | Rvalue::ThreadLocalRef(n) | 
|  | | Rvalue::AddressOf(n) | 
|  | | Rvalue::BinaryOp(n) | 
|  | | Rvalue::NullaryOp(n) => match *n {}, | 
|  | }, | 
|  | 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>( | 
|  | db: &'db dyn HirDatabase, | 
|  | owner: DefWithBodyId, | 
|  | subst: Substitution, | 
|  | trait_env: Arc<crate::TraitEnvironment<'db>>, | 
|  | ) -> Result<Arc<MirBody>, MirLowerError> { | 
|  | let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); | 
|  | let filler = &mut Filler { db, subst: &subst, trait_env, generics }; | 
|  | let body = db.mir_body(owner)?; | 
|  | let mut body = (*body).clone(); | 
|  | filler.fill_body(&mut body)?; | 
|  | Ok(Arc::new(body)) | 
|  | } | 
|  |  | 
|  | pub(crate) fn monomorphized_mir_body_cycle_result<'db>( | 
|  | _db: &'db dyn HirDatabase, | 
|  | _: DefWithBodyId, | 
|  | _: Substitution, | 
|  | _: Arc<crate::TraitEnvironment<'db>>, | 
|  | ) -> Result<Arc<MirBody>, MirLowerError> { | 
|  | Err(MirLowerError::Loop) | 
|  | } | 
|  |  | 
|  | pub fn monomorphized_mir_body_for_closure_query<'db>( | 
|  | db: &'db dyn HirDatabase, | 
|  | closure: InternedClosureId, | 
|  | subst: Substitution, | 
|  | trait_env: Arc<crate::TraitEnvironment<'db>>, | 
|  | ) -> Result<Arc<MirBody>, MirLowerError> { | 
|  | let InternedClosure(owner, _) = db.lookup_intern_closure(closure); | 
|  | let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); | 
|  | let filler = &mut Filler { db, subst: &subst, trait_env, generics }; | 
|  | let body = db.mir_body_for_closure(closure)?; | 
|  | let mut body = (*body).clone(); | 
|  | filler.fill_body(&mut body)?; | 
|  | Ok(Arc::new(body)) | 
|  | } |