1use std::sync::Arc;
8
9use cljrs_ir::{IrFunction, Repr};
10use cljrs_reader::Form;
11use cljrs_value::TypeHint;
12
13use crate::builtins::form::resolve_auto_forms;
14use crate::env::env::Env;
15
16pub fn seed_reprs_from_hints(param_hints: &[Option<TypeHint>]) -> Vec<Repr> {
23 if !param_hints.iter().any(|h| {
24 matches!(
25 h,
26 Some(
27 TypeHint::Long
28 | TypeHint::Int
29 | TypeHint::Double
30 | TypeHint::Float
31 | TypeHint::Bool
32 | TypeHint::LongArray
33 | TypeHint::DoubleArray
34 )
35 )
36 }) {
37 return Vec::new();
38 }
39 param_hints
40 .iter()
41 .map(|h| match h {
42 Some(TypeHint::Long | TypeHint::Int) => Repr::Long,
43 Some(TypeHint::Double | TypeHint::Float) => Repr::Double,
44 Some(TypeHint::Bool) => Repr::Bool,
45 Some(TypeHint::LongArray) => Repr::LongArray,
47 Some(TypeHint::DoubleArray) => Repr::DoubleArray,
48 _ => Repr::Boxed,
51 })
52 .collect()
53}
54
55#[derive(Debug)]
58pub enum LowerError {
59 LowerFailed(String),
61}
62
63impl std::fmt::Display for LowerError {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 LowerError::LowerFailed(msg) => write!(f, "lowering failed: {msg}"),
67 }
68 }
69}
70
71#[allow(clippy::too_many_arguments)]
81pub fn lower_arity(
82 name: Option<&str>,
83 params: &[Arc<str>],
84 rest_param: Option<&Arc<str>>,
85 destructure_params: &[(usize, Form)],
86 destructure_rest: Option<&Form>,
87 body: &[Form],
88 ns: &Arc<str>,
89 env: &mut Env,
90 is_async: bool,
91) -> Result<IrFunction, LowerError> {
92 lower_arity_inner(
93 name,
94 params,
95 rest_param,
96 destructure_params,
97 destructure_rest,
98 body,
99 ns,
100 env,
101 false,
102 is_async,
103 )
104 .map(|(ir, _)| ir)
105}
106
107#[allow(clippy::too_many_arguments)]
109pub fn lower_and_optimize_arity(
110 name: Option<&str>,
111 params: &[Arc<str>],
112 rest_param: Option<&Arc<str>>,
113 destructure_params: &[(usize, Form)],
114 destructure_rest: Option<&Form>,
115 body: &[Form],
116 ns: &Arc<str>,
117 env: &mut Env,
118 is_async: bool,
119) -> Result<IrFunction, LowerError> {
120 lower_and_optimize_arity_tracked(
121 name,
122 params,
123 rest_param,
124 destructure_params,
125 destructure_rest,
126 body,
127 ns,
128 env,
129 is_async,
130 )
131 .map(|(ir, _)| ir)
132}
133
134#[allow(clippy::too_many_arguments)]
138pub fn lower_and_optimize_arity_tracked(
139 name: Option<&str>,
140 params: &[Arc<str>],
141 rest_param: Option<&Arc<str>>,
142 destructure_params: &[(usize, Form)],
143 destructure_rest: Option<&Form>,
144 body: &[Form],
145 ns: &Arc<str>,
146 env: &mut Env,
147 is_async: bool,
148) -> Result<(IrFunction, Vec<(Arc<str>, Arc<str>)>), LowerError> {
149 lower_arity_inner(
150 name,
151 params,
152 rest_param,
153 destructure_params,
154 destructure_rest,
155 body,
156 ns,
157 env,
158 true,
159 is_async,
160 )
161}
162
163#[allow(clippy::too_many_arguments)]
164fn lower_arity_inner(
165 name: Option<&str>,
166 params: &[Arc<str>],
167 rest_param: Option<&Arc<str>>,
168 destructure_params: &[(usize, Form)],
169 destructure_rest: Option<&Form>,
170 body: &[Form],
171 ns: &Arc<str>,
172 env: &mut Env,
173 do_optimize: bool,
174 is_async: bool,
175) -> Result<(IrFunction, Vec<(Arc<str>, Arc<str>)>), LowerError> {
176 let prev_ns = std::mem::replace(&mut env.current_ns, ns.clone());
179 let expanded_body = macroexpand_body(body, env);
180 let resolved: Result<Vec<Form>, _> = expanded_body
184 .iter()
185 .map(|f| resolve_auto_forms(f, env))
186 .collect();
187 let expanded_body = resolved.unwrap_or(expanded_body);
188 env.current_ns = prev_ns;
189 lower_expanded_arity(
190 name,
191 params,
192 rest_param,
193 destructure_params,
194 destructure_rest,
195 &expanded_body,
196 ns,
197 env.globals.id(),
198 None,
199 do_optimize,
200 is_async,
201 )
202}
203
204pub fn macroexpand_body(body: &[Form], env: &mut Env) -> Vec<Form> {
211 use crate::tiered::apply::IR_LOWERING_ACTIVE;
213 let was_active = IR_LOWERING_ACTIVE.get();
214 IR_LOWERING_ACTIVE.set(true);
215
216 let expanded_body: Vec<Form> = body
217 .iter()
218 .map(|f| crate::interp::macros::macroexpand_all(f, env).unwrap_or_else(|_| f.clone()))
219 .collect();
220
221 IR_LOWERING_ACTIVE.with(|c| c.set(was_active));
222 expanded_body
223}
224
225#[allow(clippy::too_many_arguments)]
240pub fn lower_expanded_arity(
241 name: Option<&str>,
242 params: &[Arc<str>],
243 rest_param: Option<&Arc<str>>,
244 destructure_params: &[(usize, Form)],
245 destructure_rest: Option<&Form>,
246 expanded_body: &[Form],
247 ns: &Arc<str>,
248 globals_id: u64,
249 arity_id: Option<u64>,
250 do_optimize: bool,
251 is_async: bool,
252) -> Result<(IrFunction, Vec<(Arc<str>, Arc<str>)>), LowerError> {
253 cljrs_logging::feat_debug!(
254 "lower",
255 "lowering {:?}/{:?} optimize? {}",
256 ns,
257 name,
258 do_optimize
259 );
260
261 let mut all_params: Vec<Arc<str>> = params.to_vec();
263 if let Some(rest) = rest_param {
264 all_params.push(rest.clone());
265 }
266
267 let mut destructures: Vec<(usize, Form)> = destructure_params.to_vec();
271 if let Some(rest_pat) = destructure_rest {
272 destructures.push((params.len(), rest_pat.clone()));
273 }
274
275 let ir = cljrs_ir::lower::lower_fn_body_destructured(
276 name,
277 ns,
278 &all_params,
279 &destructures,
280 expanded_body,
281 is_async,
282 )
283 .map_err(|e| LowerError::LowerFailed(format!("{e:?}")))?;
284
285 if !do_optimize {
286 return Ok((ir, Vec::new()));
287 }
288
289 let referenced = referenced_globals(&ir);
293 let externals = match arity_id {
294 Some(id) => crate::tiered::defn_registry::snapshot_externals(globals_id, id, &referenced),
295 None => crate::tiered::defn_registry::externals_for(globals_id, &referenced),
296 };
297 let (ir, used) = cljrs_ir::lower::optimize_with_externals(ir, &externals);
298 Ok((ir, used.into_iter().collect()))
299}
300
301fn referenced_globals(ir: &IrFunction) -> std::collections::HashSet<(Arc<str>, Arc<str>)> {
304 use cljrs_ir::Inst;
305 let mut out = std::collections::HashSet::new();
306 fn walk(f: &IrFunction, out: &mut std::collections::HashSet<(Arc<str>, Arc<str>)>) {
307 for block in &f.blocks {
308 for inst in block.phis.iter().chain(block.insts.iter()) {
309 if let Inst::LoadGlobal(_, ns, name) | Inst::LoadVar(_, ns, name) = inst {
310 out.insert((ns.clone(), name.clone()));
311 }
312 }
313 }
314 for sub in &f.subfunctions {
315 walk(sub, out);
316 }
317 }
318 walk(ir, &mut out);
319 out
320}