use std::sync::Arc;
use cljrs_ir::{IrFunction, Repr};
use cljrs_reader::Form;
use cljrs_value::TypeHint;
use crate::builtins::form::resolve_auto_forms;
use crate::env::env::Env;
pub fn seed_reprs_from_hints(param_hints: &[Option<TypeHint>]) -> Vec<Repr> {
if !param_hints.iter().any(|h| {
matches!(
h,
Some(
TypeHint::Long
| TypeHint::Int
| TypeHint::Double
| TypeHint::Float
| TypeHint::Bool
| TypeHint::LongArray
| TypeHint::DoubleArray
)
)
}) {
return Vec::new();
}
param_hints
.iter()
.map(|h| match h {
Some(TypeHint::Long | TypeHint::Int) => Repr::Long,
Some(TypeHint::Double | TypeHint::Float) => Repr::Double,
Some(TypeHint::Bool) => Repr::Bool,
Some(TypeHint::LongArray) => Repr::LongArray,
Some(TypeHint::DoubleArray) => Repr::DoubleArray,
_ => Repr::Boxed,
})
.collect()
}
#[derive(Debug)]
pub enum LowerError {
LowerFailed(String),
}
impl std::fmt::Display for LowerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LowerError::LowerFailed(msg) => write!(f, "lowering failed: {msg}"),
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn lower_arity(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
destructure_params: &[(usize, Form)],
destructure_rest: Option<&Form>,
body: &[Form],
ns: &Arc<str>,
env: &mut Env,
is_async: bool,
) -> Result<IrFunction, LowerError> {
lower_arity_inner(
name,
params,
rest_param,
destructure_params,
destructure_rest,
body,
ns,
env,
false,
is_async,
)
.map(|(ir, _)| ir)
}
#[allow(clippy::too_many_arguments)]
pub fn lower_and_optimize_arity(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
destructure_params: &[(usize, Form)],
destructure_rest: Option<&Form>,
body: &[Form],
ns: &Arc<str>,
env: &mut Env,
is_async: bool,
) -> Result<IrFunction, LowerError> {
lower_and_optimize_arity_tracked(
name,
params,
rest_param,
destructure_params,
destructure_rest,
body,
ns,
env,
is_async,
)
.map(|(ir, _)| ir)
}
#[allow(clippy::too_many_arguments)]
pub fn lower_and_optimize_arity_tracked(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
destructure_params: &[(usize, Form)],
destructure_rest: Option<&Form>,
body: &[Form],
ns: &Arc<str>,
env: &mut Env,
is_async: bool,
) -> Result<(IrFunction, Vec<(Arc<str>, Arc<str>)>), LowerError> {
lower_arity_inner(
name,
params,
rest_param,
destructure_params,
destructure_rest,
body,
ns,
env,
true,
is_async,
)
}
#[allow(clippy::too_many_arguments)]
fn lower_arity_inner(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
destructure_params: &[(usize, Form)],
destructure_rest: Option<&Form>,
body: &[Form],
ns: &Arc<str>,
env: &mut Env,
do_optimize: bool,
is_async: bool,
) -> Result<(IrFunction, Vec<(Arc<str>, Arc<str>)>), LowerError> {
let prev_ns = std::mem::replace(&mut env.current_ns, ns.clone());
let expanded_body = macroexpand_body(body, env);
let resolved: Result<Vec<Form>, _> = expanded_body
.iter()
.map(|f| resolve_auto_forms(f, env))
.collect();
let expanded_body = resolved.unwrap_or(expanded_body);
env.current_ns = prev_ns;
lower_expanded_arity(
name,
params,
rest_param,
destructure_params,
destructure_rest,
&expanded_body,
ns,
env.globals.id(),
None,
do_optimize,
is_async,
)
}
pub fn macroexpand_body(body: &[Form], env: &mut Env) -> Vec<Form> {
use crate::tiered::apply::IR_LOWERING_ACTIVE;
let was_active = IR_LOWERING_ACTIVE.get();
IR_LOWERING_ACTIVE.set(true);
let expanded_body: Vec<Form> = body
.iter()
.map(|f| crate::interp::macros::macroexpand_all(f, env).unwrap_or_else(|_| f.clone()))
.collect();
IR_LOWERING_ACTIVE.with(|c| c.set(was_active));
expanded_body
}
#[allow(clippy::too_many_arguments)]
pub fn lower_expanded_arity(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
destructure_params: &[(usize, Form)],
destructure_rest: Option<&Form>,
expanded_body: &[Form],
ns: &Arc<str>,
globals_id: u64,
arity_id: Option<u64>,
do_optimize: bool,
is_async: bool,
) -> Result<(IrFunction, Vec<(Arc<str>, Arc<str>)>), LowerError> {
tracing::debug!(
target: "lower",
"lowering {:?}/{:?} optimize? {}",
ns,
name,
do_optimize
);
let mut all_params: Vec<Arc<str>> = params.to_vec();
if let Some(rest) = rest_param {
all_params.push(rest.clone());
}
let mut destructures: Vec<(usize, Form)> = destructure_params.to_vec();
if let Some(rest_pat) = destructure_rest {
destructures.push((params.len(), rest_pat.clone()));
}
let ir = cljrs_ir::lower::lower_fn_body_destructured(
name,
ns,
&all_params,
&destructures,
expanded_body,
is_async,
)
.map_err(|e| LowerError::LowerFailed(format!("{e:?}")))?;
if !do_optimize {
return Ok((ir, Vec::new()));
}
let referenced = referenced_globals(&ir);
let externals = match arity_id {
Some(id) => crate::tiered::defn_registry::snapshot_externals(globals_id, id, &referenced),
None => crate::tiered::defn_registry::externals_for(globals_id, &referenced),
};
let (ir, used) = cljrs_ir::lower::optimize_with_externals(ir, &externals);
Ok((ir, used.into_iter().collect()))
}
fn referenced_globals(ir: &IrFunction) -> std::collections::HashSet<(Arc<str>, Arc<str>)> {
use cljrs_ir::Inst;
let mut out = std::collections::HashSet::new();
fn walk(f: &IrFunction, out: &mut std::collections::HashSet<(Arc<str>, Arc<str>)>) {
for block in &f.blocks {
for inst in block.phis.iter().chain(block.insts.iter()) {
if let Inst::LoadGlobal(_, ns, name) | Inst::LoadVar(_, ns, name) = inst {
out.insert((ns.clone(), name.clone()));
}
}
}
for sub in &f.subfunctions {
walk(sub, out);
}
}
walk(ir, &mut out);
out
}