use crate::error::CompilerError;
use crate::ir::{EnumId, GenericBase, IrExpr, IrModule, ResolvedType, StructId, TraitId};
use crate::location::Span;
use super::expr_walk::iter_expr_children_mut;
use super::walkers::walk_module_types_mut;
pub(super) fn build_struct_remap(module: &IrModule) -> Vec<Option<StructId>> {
let mut out = Vec::with_capacity(module.structs.len());
let mut next: u32 = 0;
for s in &module.structs {
if s.generic_params.is_empty() {
out.push(Some(StructId(next)));
next = next.saturating_add(1);
} else {
out.push(None);
}
}
out
}
pub(super) fn build_enum_remap(module: &IrModule) -> Vec<Option<EnumId>> {
let mut out = Vec::with_capacity(module.enums.len());
let mut next: u32 = 0;
for e in &module.enums {
if e.generic_params.is_empty() {
out.push(Some(EnumId(next)));
next = next.saturating_add(1);
} else {
out.push(None);
}
}
out
}
pub(super) fn build_trait_remap(module: &IrModule) -> Vec<Option<TraitId>> {
let mut out = Vec::with_capacity(module.traits.len());
let mut next: u32 = 0;
for t in &module.traits {
if t.generic_params.is_empty() {
out.push(Some(TraitId(next)));
next = next.saturating_add(1);
} else {
out.push(None);
}
}
out
}
pub(super) fn drop_specialised_generic_impls(
module: &mut IrModule,
struct_remap: &[Option<StructId>],
enum_remap: &[Option<EnumId>],
) -> Vec<Option<usize>> {
let keep: Vec<bool> = module
.impls
.iter()
.map(|imp| match imp.target {
crate::ir::ImplTarget::Struct(id) => struct_remap
.get(id.0 as usize)
.copied()
.is_none_or(|slot| slot.is_some()),
crate::ir::ImplTarget::Enum(id) => enum_remap
.get(id.0 as usize)
.copied()
.is_none_or(|slot| slot.is_some()),
crate::ir::ImplTarget::Primitive(_) => true,
})
.collect();
let mut new_index: Vec<Option<usize>> = Vec::with_capacity(keep.len());
let mut next: usize = 0;
for &k in &keep {
if k {
new_index.push(Some(next));
next = next.saturating_add(1);
} else {
new_index.push(None);
}
}
let mut idx = 0;
module.impls.retain(|_| {
let k = keep.get(idx).copied().unwrap_or(false);
idx = idx.saturating_add(1);
k
});
new_index
}
fn impl_index_rewrite_expr(expr: &mut IrExpr, remap: &[Option<usize>]) {
use crate::ir::{DispatchKind, ImplId};
for child in iter_expr_children_mut(expr) {
impl_index_rewrite_expr(child, remap);
}
if let IrExpr::MethodCall {
dispatch: DispatchKind::Static { impl_id },
..
} = expr
{
if let Some(Some(new)) = remap.get(impl_id.0 as usize).copied() {
*impl_id = ImplId(u32::try_from(new).unwrap_or(u32::MAX));
}
}
}
pub(super) fn apply_impl_index_remap(module: &mut IrModule, remap: &[Option<usize>]) {
let identity = remap
.iter()
.enumerate()
.all(|(i, s)| matches!(s, Some(j) if *j == i));
if identity {
return;
}
for func in &mut module.functions {
if let Some(body) = &mut func.body {
impl_index_rewrite_expr(body, remap);
}
}
for imp in &mut module.impls {
for func in &mut imp.functions {
if let Some(body) = &mut func.body {
impl_index_rewrite_expr(body, remap);
}
}
}
for s in &mut module.structs {
for field in &mut s.fields {
if let Some(default) = &mut field.default {
impl_index_rewrite_expr(default, remap);
}
}
}
for l in &mut module.lets {
impl_index_rewrite_expr(&mut l.value, remap);
}
}
#[expect(
clippy::too_many_lines,
reason = "linear walk over every TraitId-bearing slot in the module"
)]
pub(super) fn apply_remaps(
module: &mut IrModule,
struct_remap: &[Option<StructId>],
enum_remap: &[Option<EnumId>],
trait_remap: &[Option<TraitId>],
) -> Result<(), Vec<CompilerError>> {
walk_module_types_mut(module, |ty| {
remap_type(ty, struct_remap, enum_remap, trait_remap);
});
let mut errors: Vec<CompilerError> = Vec::new();
let remap_trait_id_in_place = |id: &mut TraitId, errors: &mut Vec<CompilerError>| {
match trait_remap.get(id.0 as usize).copied() {
Some(Some(new)) => *id = new,
Some(None) => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: stale TraitId({}) survived rewrite_trait_refs (generic trait dropped during compaction)",
id.0
),
span: Span::default(),
}),
None => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: TraitId({}) out of bounds for trait remap table (len {})",
id.0,
trait_remap.len()
),
span: Span::default(),
}),
}
};
for s in &mut module.structs {
s.traits.retain_mut(
|tr| match trait_remap.get(tr.trait_id.0 as usize).copied() {
Some(Some(new)) => {
tr.trait_id = new;
true
}
Some(None) | None => false,
},
);
for gp in &mut s.generic_params {
for c in &mut gp.constraints {
remap_trait_id_in_place(&mut c.trait_id, &mut errors);
}
}
}
for t in &mut module.traits {
for id in &mut t.composed_traits {
remap_trait_id_in_place(id, &mut errors);
}
for gp in &mut t.generic_params {
for c in &mut gp.constraints {
remap_trait_id_in_place(&mut c.trait_id, &mut errors);
}
}
}
for e in &mut module.enums {
for gp in &mut e.generic_params {
for c in &mut gp.constraints {
remap_trait_id_in_place(&mut c.trait_id, &mut errors);
}
}
}
for f in &mut module.functions {
for gp in &mut f.generic_params {
for c in &mut gp.constraints {
remap_trait_id_in_place(&mut c.trait_id, &mut errors);
}
}
}
for imp in &mut module.impls {
match &mut imp.target {
crate::ir::ImplTarget::Struct(id) => match struct_remap.get(id.0 as usize).copied() {
Some(Some(new)) => *id = new,
Some(None) => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: impl block targets struct id {} which was dropped during compaction (drop_specialised_generic_impls missed it)",
id.0
),
span: Span::default(),
}),
None => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: impl block targets struct id {} which is out of bounds for the remap table (len {})",
id.0,
struct_remap.len()
),
span: Span::default(),
}),
},
crate::ir::ImplTarget::Enum(id) => match enum_remap.get(id.0 as usize).copied() {
Some(Some(new)) => *id = new,
Some(None) => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: impl block targets enum id {} which was dropped during compaction (drop_specialised_generic_impls missed it)",
id.0
),
span: Span::default(),
}),
None => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: impl block targets enum id {} which is out of bounds for the remap table (len {})",
id.0,
enum_remap.len()
),
span: Span::default(),
}),
},
crate::ir::ImplTarget::Primitive(_) => {}
}
if let Some(tr) = &mut imp.trait_ref {
remap_trait_id_in_place(&mut tr.trait_id, &mut errors);
}
for gp in &mut imp.generic_params {
for c in &mut gp.constraints {
remap_trait_id_in_place(&mut c.trait_id, &mut errors);
}
}
}
for f in &mut module.functions {
if let Some(body) = &mut f.body {
walk_dispatch(body, trait_remap, &mut errors);
}
}
for imp in &mut module.impls {
for f in &mut imp.functions {
if let Some(body) = &mut f.body {
walk_dispatch(body, trait_remap, &mut errors);
}
}
}
for l in &mut module.lets {
walk_dispatch(&mut l.value, trait_remap, &mut errors);
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
fn walk_dispatch(
expr: &mut IrExpr,
trait_remap: &[Option<TraitId>],
errors: &mut Vec<CompilerError>,
) {
for child in iter_expr_children_mut(expr) {
walk_dispatch(child, trait_remap, errors);
}
if let IrExpr::MethodCall {
dispatch: crate::ir::DispatchKind::Virtual { trait_id, .. },
..
} = expr
{
match trait_remap.get(trait_id.0 as usize).copied() {
Some(Some(new)) => *trait_id = new,
Some(None) => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: Virtual dispatch references generic-trait id {} that was dropped",
trait_id.0
),
span: Span::default(),
}),
None => errors.push(CompilerError::InternalError {
detail: format!(
"monomorphise: Virtual dispatch trait id {} out of bounds for trait remap (len {})",
trait_id.0,
trait_remap.len()
),
span: Span::default(),
}),
}
}
}
fn remap_type(
ty: &mut ResolvedType,
struct_remap: &[Option<StructId>],
enum_remap: &[Option<EnumId>],
trait_remap: &[Option<TraitId>],
) {
match ty {
ResolvedType::Struct(id) => {
if let Some(Some(new)) = struct_remap.get(id.0 as usize).copied() {
*id = new;
}
}
ResolvedType::Enum(id) => {
if let Some(Some(new)) = enum_remap.get(id.0 as usize).copied() {
*id = new;
}
}
ResolvedType::Trait(id) => {
if let Some(Some(new)) = trait_remap.get(id.0 as usize).copied() {
*id = new;
}
}
ResolvedType::Array(inner) | ResolvedType::Range(inner) | ResolvedType::Optional(inner) => {
remap_type(inner, struct_remap, enum_remap, trait_remap);
}
ResolvedType::Tuple(fields) => {
for (_, t) in fields {
remap_type(t, struct_remap, enum_remap, trait_remap);
}
}
ResolvedType::Dictionary { key_ty, value_ty } => {
remap_type(key_ty, struct_remap, enum_remap, trait_remap);
remap_type(value_ty, struct_remap, enum_remap, trait_remap);
}
ResolvedType::Closure {
param_tys,
return_ty,
} => {
for (_, t) in param_tys {
remap_type(t, struct_remap, enum_remap, trait_remap);
}
remap_type(return_ty, struct_remap, enum_remap, trait_remap);
}
ResolvedType::Generic { base, args } => {
match base {
GenericBase::Struct(id) => {
if let Some(Some(new)) = struct_remap.get(id.0 as usize).copied() {
*id = new;
}
}
GenericBase::Enum(id) => {
if let Some(Some(new)) = enum_remap.get(id.0 as usize).copied() {
*id = new;
}
}
GenericBase::Trait(id) => {
if let Some(Some(new)) = trait_remap.get(id.0 as usize).copied() {
*id = new;
}
}
}
for a in args {
remap_type(a, struct_remap, enum_remap, trait_remap);
}
}
ResolvedType::External { type_args, .. } => {
for a in type_args {
remap_type(a, struct_remap, enum_remap, trait_remap);
}
}
ResolvedType::Primitive(_) | ResolvedType::TypeParam(_) | ResolvedType::Error => {}
}
}