blob: c565228d91e76b503b2d40260e38af73122290e9 [file] [log] [blame]
//! 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;
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::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)
}