352 lines
14 KiB
Rust
352 lines
14 KiB
Rust
|
//! 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)
|
||
|
}
|