use crate::ir::{GenericBase, IrExpr, IrModule, ResolvedType};
use super::expr_walk::walk_expr;
use super::rewrite::receiver_to_base;
use super::walkers::walk_module_types;
#[derive(Default)]
pub(super) struct LeftoverScanner {
first: Option<String>,
}
impl LeftoverScanner {
fn note(&mut self, detail: String) {
if self.first.is_none() {
self.first = Some(detail);
}
}
pub(super) fn first_error(self) -> Option<String> {
self.first
.map(|s| format!("monomorphise: leftover after pass — {s}"))
}
pub(super) fn scan(&mut self, module: &IrModule) {
for t in &module.traits {
if !t.generic_params.is_empty() {
self.note(format!(
"generic trait `{}` survived compaction (rewrite_trait_refs missed a reference)",
t.name
));
}
}
let mut check = |ty: &ResolvedType| {
if let Some(sample) = first_leftover(ty) {
self.note(sample);
}
};
walk_module_types(module, &mut check);
scan_dispatch_leftovers(module, self);
}
}
fn scan_dispatch_leftovers(module: &IrModule, scanner: &mut LeftoverScanner) {
let mut check = |expr: &IrExpr| {
if let IrExpr::MethodCall {
receiver,
method,
dispatch: crate::ir::DispatchKind::Virtual { trait_id, .. },
..
} = expr
{
if let Some(base) = receiver_to_base(receiver.ty()) {
let kind = match base {
GenericBase::Struct(id) => format!("struct id {}", id.0),
GenericBase::Enum(id) => format!("enum id {}", id.0),
GenericBase::Trait(id) => format!("trait id {}", id.0),
};
scanner.note(format!(
"unresolved Virtual dispatch — method `{method}` on concrete receiver \
({kind}) for trait id {} (devirtualisation should have rewritten this)",
trait_id.0
));
}
}
};
for f in &module.functions {
if let Some(body) = &f.body {
walk_expr(body, &mut check);
}
}
for imp in &module.impls {
for f in &imp.functions {
if let Some(body) = &f.body {
walk_expr(body, &mut check);
}
}
}
for l in &module.lets {
walk_expr(&l.value, &mut check);
}
}
fn first_leftover(ty: &ResolvedType) -> Option<String> {
match ty {
ResolvedType::TypeParam(name) => Some(format!("unresolved TypeParam(`{name}`)")),
ResolvedType::Generic { base, args } => {
let (kind, id) = match base {
GenericBase::Struct(s) => ("struct", s.0),
GenericBase::Enum(e) => ("enum", e.0),
GenericBase::Trait(t) => ("trait", t.0),
};
Some(format!(
"unresolved Generic(base={kind}_id={id}, {} args)",
args.len()
))
}
ResolvedType::Array(inner) | ResolvedType::Range(inner) | ResolvedType::Optional(inner) => {
first_leftover(inner)
}
ResolvedType::Tuple(fields) => fields.iter().find_map(|(_, t)| first_leftover(t)),
ResolvedType::Dictionary { key_ty, value_ty } => {
first_leftover(key_ty).or_else(|| first_leftover(value_ty))
}
ResolvedType::Closure {
param_tys,
return_ty,
} => param_tys
.iter()
.find_map(|(_, t)| first_leftover(t))
.or_else(|| first_leftover(return_ty)),
ResolvedType::External { type_args, .. } => type_args.iter().find_map(first_leftover),
ResolvedType::Error => Some("ResolvedType::Error placeholder".to_string()),
ResolvedType::Primitive(_)
| ResolvedType::Struct(_)
| ResolvedType::Trait(_)
| ResolvedType::Enum(_) => None,
}
}