use std::collections::HashMap;
use crate::Result;
use crate::diagnostic::Diagnostic;
use crate::intermediate::fold::{IrFold, fold_ty};
use crate::utils::toposort;
use indexmap::IndexMap;
use itertools::Itertools;
use lutra_bin::ir;
#[tracing::instrument(skip_all, name = "layouter")]
pub fn on_program(mut program: ir::Program) -> ir::Program {
tracing::trace!(
"types: {:?}",
program.defs.iter().map(|x| &x.name).collect_vec()
);
let mut l = Layouter::default();
program.defs = order_ty_defs(program.defs);
program.defs = l.compute_ty_defs_layout(program.defs).unwrap();
program.main = l.fold_expr(program.main).unwrap();
program
}
#[tracing::instrument(skip_all, name = "layouter")]
pub fn on_root_module(root_module: ir::Module) -> ir::Module {
let mut l = Layouter::default();
let mut vars = Vec::new();
let mut types = Vec::new();
for (path, def) in root_module.iter_defs_re() {
match def {
ir::Decl::Mod(_) => unreachable!(),
ir::Decl::Ty(ty) => {
let def = ir::TyDef {
name: path,
ty: ty.clone(),
};
types.push(def);
}
ir::Decl::Var(ty) => {
vars.push((path, ty.clone()));
}
}
}
let types = order_ty_defs(types);
let types = l.compute_ty_defs_layout(types).unwrap();
let mut result = ir::Module { decls: Vec::new() };
for (path, var) in vars {
let var = l.fold_ty(var).unwrap();
result.insert(&path.0, ir::Decl::Var(var));
}
for def in types {
result.insert(&def.name.0, ir::Decl::Ty(def.ty));
}
result
}
#[derive(Default)]
struct Layouter {
layouts: IndexMap<ir::Path, ir::TyLayout>,
contains_missing_layout: bool,
}
impl Layouter {
fn compute_ty_defs_layout(
&mut self,
defs: Vec<ir::TyDef>,
) -> Result<Vec<ir::TyDef>, Diagnostic> {
let mut done_1 = Vec::with_capacity(defs.len());
let mut redo = Vec::with_capacity(defs.len());
for def in defs {
self.contains_missing_layout = false;
done_1.push(self.compute_ty_def_layout(def)?);
redo.push(self.contains_missing_layout);
}
let mut done_2 = Vec::with_capacity(done_1.len());
for (def, redo) in std::iter::zip(done_1, redo) {
self.contains_missing_layout = false;
done_2.push(if redo {
self.compute_ty_def_layout(def)?
} else {
def
});
assert!(!self.contains_missing_layout);
}
Ok(done_2)
}
fn compute_ty_def_layout(&mut self, mut def: ir::TyDef) -> Result<ir::TyDef, Diagnostic> {
tracing::debug!("computing layout of {:?}", &def.name);
def.ty = self.fold_ty(def.ty).unwrap();
let Some(layout) = def.ty.layout.clone() else {
panic!("cannot compute layout of top-level type def?")
};
self.layouts.insert(def.name.clone(), layout);
Ok(def)
}
}
impl IrFold for Layouter {
#[tracing::instrument(skip_all, name = "t")]
fn fold_ty(&mut self, ty: ir::Ty) -> Result<ir::Ty, ()> {
let mut ty = fold_ty(self, ty)?;
self.compute_ty_layout(&mut ty);
Ok(ty)
}
}
impl Layouter {
fn compute_ty_layout(&mut self, ty: &mut ir::Ty) {
if ty.layout.is_some() {
return;
}
if let ir::TyKind::Ident(path) = &ty.kind {
let layout = self.layouts.get(path);
ty.layout = layout.cloned();
} else {
ty.layout = lutra_bin::layout::compute(ty);
}
if ty.layout.is_none() && !ty.kind.is_function() {
self.contains_missing_layout = true;
tracing::debug!("missing layout: {}\n {ty:?}", ir::print_ty(ty));
}
}
}
fn order_ty_defs(defs: Vec<ir::TyDef>) -> Vec<ir::TyDef> {
let mut deps: Vec<(ir::Path, Vec<ir::Path>)> = vec![];
for def in &defs {
let mut refs = Vec::new();
collect_refs(&def.ty, &mut refs);
deps.push((def.name.clone(), refs));
}
let order = toposort(&deps);
let mut by_name: HashMap<_, _> = defs.into_iter().map(|d| (d.name, d.ty)).collect();
let mut ordered = Vec::with_capacity(by_name.len());
for group in order {
assert!(group.len() == 1);
let (name, ty) = by_name.remove_entry(group[0]).unwrap();
ordered.push(ir::TyDef { name, ty });
}
ordered
}
pub fn collect_refs(ty: &ir::Ty, refs: &mut Vec<ir::Path>) {
match &ty.kind {
ir::TyKind::Ident(ident) => refs.push(ident.clone()),
ir::TyKind::Primitive(_) => {}
ir::TyKind::Tuple(fields) => {
for f in fields {
collect_refs(&f.ty, refs);
}
}
ir::TyKind::Array(_) => {
}
ir::TyKind::Enum(variants) => {
for (i, v) in variants.iter().enumerate() {
if ty.variants_recursive.contains(&(i as u16)) {
continue;
}
collect_refs(&v.ty, refs);
}
}
ir::TyKind::Function(_) => panic!("cannot layout a function"),
}
}