use std::collections::HashMap;
use crate::ir::{IrExpr, IrModule};
use super::super::walkers::walk_expr_children_mut;
use super::naming::{imported_path_of, qualified_name};
pub(in crate::ir::monomorphise) fn qualify_imported_paths(
module: &mut IrModule,
imported_modules: &HashMap<Vec<String>, IrModule>,
) {
if imported_modules.is_empty() {
return;
}
let entry_imports = module.imports.clone();
let func_sources: Vec<Option<Vec<String>>> = module
.functions
.iter()
.map(|f| imported_path_of(&f.name, imported_modules))
.collect();
let impl_method_sources: Vec<Vec<Option<Vec<String>>>> = module
.impls
.iter()
.map(|imp| {
let impl_source = match imp.target {
crate::ir::ImplTarget::Struct(id) => module
.get_struct(id)
.and_then(|s| imported_path_of(&s.name, imported_modules)),
crate::ir::ImplTarget::Enum(id) => module
.get_enum(id)
.and_then(|e| imported_path_of(&e.name, imported_modules)),
crate::ir::ImplTarget::Primitive(_) => None,
};
imp.functions
.iter()
.map(|m| {
impl_source
.clone()
.or_else(|| imported_path_of(&m.name, imported_modules))
})
.collect()
})
.collect();
let let_sources: Vec<Option<Vec<String>>> = module
.lets
.iter()
.map(|l| imported_path_of(&l.name, imported_modules))
.collect();
for (func, source) in module.functions.iter_mut().zip(func_sources.iter()) {
if let Some(body) = &mut func.body {
let context =
build_qualification_context(source.as_deref(), &entry_imports, imported_modules);
if !context.is_empty() {
qualify_paths_in_expr(body, &context);
}
}
}
for (impl_block, method_sources) in module.impls.iter_mut().zip(impl_method_sources.iter()) {
for (method, source) in impl_block.functions.iter_mut().zip(method_sources.iter()) {
if let Some(body) = &mut method.body {
let context = build_qualification_context(
source.as_deref(),
&entry_imports,
imported_modules,
);
if !context.is_empty() {
qualify_paths_in_expr(body, &context);
}
}
}
}
for (let_binding, source) in module.lets.iter_mut().zip(let_sources.iter()) {
let context =
build_qualification_context(source.as_deref(), &entry_imports, imported_modules);
if !context.is_empty() {
qualify_paths_in_expr(&mut let_binding.value, &context);
}
}
}
fn build_qualification_context(
source: Option<&[String]>,
entry_imports: &[crate::ir::IrImport],
imported_modules: &HashMap<Vec<String>, IrModule>,
) -> HashMap<String, String> {
let mut map: HashMap<String, String> = HashMap::new();
if let Some(helper_path) = source {
let Some(helper) = imported_modules.get(helper_path) else {
return map;
};
let prefix = qualified_name(helper_path, "");
for f in &helper.functions {
map.entry(f.name.clone())
.or_insert_with(|| format!("{prefix}{}", f.name));
}
for s in &helper.structs {
map.entry(s.name.clone())
.or_insert_with(|| format!("{prefix}{}", s.name));
}
for e in &helper.enums {
map.entry(e.name.clone())
.or_insert_with(|| format!("{prefix}{}", e.name));
}
for t in &helper.traits {
map.entry(t.name.clone())
.or_insert_with(|| format!("{prefix}{}", t.name));
}
for l in &helper.lets {
map.entry(l.name.clone())
.or_insert_with(|| format!("{prefix}{}", l.name));
}
register_imports_into(&helper.imports, &mut map);
} else {
register_imports_into(entry_imports, &mut map);
}
map
}
fn register_imports_into(imports: &[crate::ir::IrImport], map: &mut HashMap<String, String>) {
for imp in imports {
let prefix = qualified_name(&imp.module_path, "");
for item in &imp.items {
map.entry(item.name.clone())
.or_insert_with(|| format!("{prefix}{}", item.name));
}
}
}
fn qualify_paths_in_expr(expr: &mut IrExpr, bare_to_qualified: &HashMap<String, String>) {
qualify_self_path(expr, bare_to_qualified);
walk_expr_children_mut(expr, &mut |child| {
qualify_paths_in_expr(child, bare_to_qualified);
});
}
fn qualify_self_path(expr: &mut IrExpr, bare_to_qualified: &HashMap<String, String>) {
let path: &mut Vec<String> = match expr {
IrExpr::FunctionCall {
path,
function_id: None,
..
}
| IrExpr::Reference {
path,
target: crate::ir::ReferenceTarget::Unresolved,
..
} => path,
IrExpr::FunctionCall { .. }
| IrExpr::Reference { .. }
| IrExpr::Literal { .. }
| IrExpr::StructInst { .. }
| IrExpr::EnumInst { .. }
| IrExpr::Array { .. }
| IrExpr::Tuple { .. }
| IrExpr::SelfFieldRef { .. }
| IrExpr::FieldAccess { .. }
| IrExpr::LetRef { .. }
| IrExpr::BinaryOp { .. }
| IrExpr::UnaryOp { .. }
| IrExpr::If { .. }
| IrExpr::For { .. }
| IrExpr::Match { .. }
| IrExpr::CallClosure { .. }
| IrExpr::MethodCall { .. }
| IrExpr::Closure { .. }
| IrExpr::ClosureRef { .. }
| IrExpr::DictLiteral { .. }
| IrExpr::DictAccess { .. }
| IrExpr::Block { .. } => return,
};
if let [single] = path.as_slice() {
if let Some(qualified) = bare_to_qualified.get(single) {
*path = qualified.split("::").map(String::from).collect();
}
}
}