Skip to main content

cljrs_runtime/interp/
special.rs

1//! Special form evaluators.
2
3use std::collections::{HashMap, HashSet};
4use std::sync::Arc;
5
6use crate::builtins::form::{
7    expand_pairs, expand_reader_conds, expand_reader_conds_cow, form_to_value, resolve_auto_forms,
8    select_reader_cond,
9};
10use crate::env::env::{Env, RequireRefer, RequireSpec};
11use crate::env::error::{EvalError, EvalResult};
12use crate::env::loader::load_ns;
13use crate::interp::destructure::bind_pattern;
14use crate::interp::eval::{eval, eval_body, is_special_form};
15use cljrs_gc::GcPtr;
16use cljrs_reader::Form;
17use cljrs_reader::form::FormKind;
18use cljrs_value::error::ExceptionInfo;
19use cljrs_value::{
20    CljxFn, CljxFnArity, CljxFuture, FutureState, Keyword, MapValue, Protocol, ProtocolMethod,
21    ReferClojureFilter, TypeHint, Value, ValueError,
22};
23
24/// Dispatch to the right special-form handler.
25pub fn eval_special(head: &str, args: &[Form], env: &mut Env) -> EvalResult {
26    crate::env::policy::check_special(head)?;
27    match head {
28        "def" => eval_def(args, env),
29        "fn*" | "fn" => eval_fn(args, env),
30        "if" => eval_if(args, env),
31        "do" => eval_body(args, env),
32        "let*" | "let" => eval_let(args, env),
33        "loop*" | "loop" => eval_loop(args, env),
34        "recur" => eval_recur(args, env),
35        "quote" => eval_quote(args, env),
36        "var" => eval_var(args, env),
37        "set!" => eval_set_bang(args, env),
38        "throw" => eval_throw(args, env),
39        "try" => eval_try(args, env),
40        "defn" => eval_defn(args, env, false),
41        "defn-" => eval_defn(args, env, true),
42        "defmacro" => eval_defmacro(args, env),
43        "defonce" => eval_defonce(args, env),
44        "and" => eval_and(args, env),
45        "or" => eval_or(args, env),
46        "." => Err(EvalError::Runtime("interop not yet implemented".into())),
47        "ns" => eval_ns(args, env),
48        "require" => eval_require(args, env),
49        "letfn" => eval_letfn(args, env),
50        "in-ns" => eval_in_ns(args, env),
51        "alias" => eval_alias(args, env),
52        "protocol*" => eval_protocol_star(args, env),
53        "deftype*" => eval_deftype_star(args, env),
54        "load-file" => eval_load_file(args, env),
55        "binding" => eval_binding(args, env),
56        "with-out-str" => eval_with_out_str(args, env),
57        "await" => eval_await(args, env),
58        _ => unreachable!("unknown special form: {head}"),
59    }
60}
61
62// ── def ───────────────────────────────────────────────────────────────────────
63
64fn eval_def(args: &[Form], env: &mut Env) -> EvalResult {
65    let target = parse_def(args, env)?;
66    let val = match target.value_form {
67        Some(form) => {
68            // Under no-gc: def value expressions go to the StaticArena since the
69            // Var must outlive all scratch regions.
70            #[cfg(feature = "no-gc")]
71            let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
72            eval(form, env)?
73        }
74        None => Value::Nil,
75    };
76    intern_def(target, val, env)
77}
78
79/// A parsed `(def name "doc"? value?)`: everything but the value itself.
80///
81/// Public, together with [`parse_def`] and [`intern_def`], so the async
82/// evaluator (`cljrs-async`) can evaluate `value_form` with a yielding
83/// evaluator and still share the rest of `def`.
84pub struct DefTarget<'a> {
85    pub name: String,
86    /// Metadata from `^meta` on the name and the docstring, merged.
87    pub meta: Option<Value>,
88    /// The value expression; `None` for `(def name)`.
89    pub value_form: Option<&'a Form>,
90}
91
92/// Parse the name, metadata and docstring of a `def` without evaluating its
93/// value expression. `^{...}` metadata on the name *is* evaluated here.
94pub fn parse_def<'a>(args: &'a [Form], env: &mut Env) -> EvalResult<DefTarget<'a>> {
95    if args.is_empty() {
96        return Err(EvalError::Runtime("def requires a name".into()));
97    }
98    let (name, meta_opt) = extract_def_name(&args[0], env)?;
99    // Optional docstring: (def name "docstring" value)
100    let (docstring, val_idx) = if args.len() > 2
101        && let FormKind::Str(s) = &args[1].kind
102    {
103        (Some(s.clone()), 2)
104    } else {
105        (None, 1)
106    };
107    Ok(DefTarget {
108        name,
109        meta: merge_meta(meta_opt, docstring.as_deref().map(doc_meta)),
110        value_form: args.get(val_idx),
111    })
112}
113
114/// Intern `val` under a parsed `def` target in the current namespace.
115pub fn intern_def(target: DefTarget<'_>, val: Value, env: &mut Env) -> EvalResult {
116    let var = env
117        .globals
118        .intern(&env.current_ns, Arc::from(target.name.as_str()), val);
119    if let Some(meta_val) = target.meta {
120        var.get().set_meta(meta_val);
121    }
122    Ok(Value::Var(var))
123}
124
125/// The already-bound var a `defonce` named `name` would leave untouched, if
126/// any.
127pub fn defonce_existing(name: &str, env: &Env) -> Option<Value> {
128    env.globals
129        .lookup_var(&env.current_ns, name)
130        .filter(|var| var.get().is_bound())
131        .map(Value::Var)
132}
133
134/// Build a `{:doc "..."}` metadata map fragment for a docstring.
135fn doc_meta(doc: &str) -> Value {
136    Value::Map(MapValue::empty().assoc(
137        Value::keyword(Keyword::parse("doc")),
138        Value::string(doc.to_string()),
139    ))
140}
141
142fn private_meta() -> Value {
143    Value::Map(
144        MapValue::empty().assoc(Value::keyword(Keyword::parse("private")), Value::Bool(true)),
145    )
146}
147
148/// Build a `{:arglists ([x] [x y & more])}` metadata fragment from a
149/// `Value::Fn`/`Value::Macro`'s parsed arities. `skip` elides leading fixed
150/// params that aren't part of the public signature (defmacro's implicit
151/// `&form`/`&env`).
152fn arglists_meta(fn_val: &Value, skip: usize) -> Option<Value> {
153    let arities = match fn_val {
154        Value::Fn(f) => &f.get().arities,
155        Value::Macro(f) => &f.get().arities,
156        _ => return None,
157    };
158    let lists: Vec<Value> = arities
159        .iter()
160        .map(|a| {
161            let mut syms: Vec<Value> = a
162                .params
163                .iter()
164                .skip(skip)
165                .map(|p| Value::symbol(cljrs_value::Symbol::simple(p.as_ref())))
166                .collect();
167            if let Some(rest) = &a.rest_param {
168                syms.push(Value::symbol(cljrs_value::Symbol::simple("&")));
169                syms.push(Value::symbol(cljrs_value::Symbol::simple(rest.as_ref())));
170            }
171            Value::Vector(GcPtr::new(cljrs_value::PersistentVector::from_iter(syms)))
172        })
173        .collect();
174    Some(Value::Map(MapValue::empty().assoc(
175        Value::keyword(Keyword::parse("arglists")),
176        Value::Vector(GcPtr::new(cljrs_value::PersistentVector::from_iter(lists))),
177    )))
178}
179
180/// Extract the def name and optional metadata from the name form.
181fn extract_def_name(form: &Form, env: &mut Env) -> EvalResult<(String, Option<Value>)> {
182    match &form.kind {
183        FormKind::Symbol(s) => Ok((s.clone(), None)),
184        // `^:a ^:b x` nests `Meta` forms; unwrap all, outer mark winning.
185        FormKind::Meta(meta_form, inner) => {
186            let meta_val = compile_meta_form(meta_form, env)?;
187            let (name, inner_meta) = extract_def_name(inner, env)?;
188            Ok((name, merge_meta(inner_meta, Some(meta_val))))
189        }
190        _ => Err(EvalError::Runtime("def name must be a symbol".into())),
191    }
192}
193
194/// Expand a metadata shorthand form into a map value, evaluating the general
195/// case (`^{:x (+ 1 2)}` → `{:x 3}`).
196///
197/// The shorthand table itself lives in [`crate::builtins::form`] and is shared
198/// with the quoted position, which differs only in resolving that general case
199/// as data instead.
200pub fn compile_meta_form(meta: &Form, env: &mut Env) -> EvalResult<Value> {
201    crate::builtins::form::expand_meta_annotation(meta, &mut |f| eval(f, env))
202}
203
204// ── fn* ───────────────────────────────────────────────────────────────────────
205
206/// Does a `^meta` form (or metadata map literal) request `:async`?
207///
208/// Handles the keyword shorthand `^:async` (a bare `:async` keyword form) and
209/// an explicit map such as `^{:async true}` or a `defn` attr-map `{:async true}`.
210pub fn meta_form_is_async(meta: &Form) -> bool {
211    match &meta.kind {
212        FormKind::Keyword(k) => k == "async",
213        FormKind::Map(entries) => entries.chunks(2).any(|kv| {
214            matches!(&kv[0].kind, FormKind::Keyword(k) if k == "async")
215                && !matches!(
216                    kv.get(1).map(|f| &f.kind),
217                    None | Some(FormKind::Bool(false)) | Some(FormKind::Nil)
218                )
219        }),
220        _ => false,
221    }
222}
223
224fn eval_fn(args: &[Form], env: &mut Env) -> EvalResult {
225    // Peel any leading `^meta` wrappers, e.g. `(fn ^:async [..] ..)` or
226    // `(fn ^:async name [..] ..)`, recording whether `:async` was requested.
227    let mut is_async = false;
228    let peeled: Vec<Form>;
229    let args: &[Form] = if matches!(args.first().map(|f| &f.kind), Some(FormKind::Meta(..))) {
230        let (metas, head) = args[0].peel_meta();
231        is_async |= metas.iter().any(|m| meta_form_is_async(m));
232        peeled = std::iter::once(head.clone())
233            .chain(args[1..].iter().cloned())
234            .collect();
235        &peeled
236    } else {
237        args
238    };
239
240    let mut idx = 0;
241    let mut name: Option<Arc<str>> = None;
242
243    // Optional name.
244    if let Some(FormKind::Symbol(s)) = args.first().map(|f| &f.kind)
245        && !is_special_form(s)
246    {
247        name = Some(Arc::from(s.as_str()));
248        idx = 1;
249    }
250
251    let rest = &args[idx..];
252    if rest.is_empty() {
253        return Err(EvalError::Runtime("fn* requires params and body".into()));
254    }
255
256    // A `^hint` on the params vector or on an arity clause is advisory — the
257    // structural shape is the form underneath it.
258    let arities = match &rest[0].unmeta().kind {
259        FormKind::Vector(_) => {
260            // Single arity: (fn* [params] body...)
261            vec![parse_arity(&rest[0], &rest[1..])?]
262        }
263        FormKind::List(_) => {
264            // Multi-arity: (fn* ([params] body...) ...)
265            rest.iter()
266                .map(|arity_form| {
267                    if let Some(forms) = arity_form.as_list() {
268                        if forms.is_empty() {
269                            return Err(EvalError::Runtime("arity clause requires params".into()));
270                        }
271                        parse_arity(&forms[0], &forms[1..])
272                    } else {
273                        Err(EvalError::Runtime("expected arity clause (list)".into()))
274                    }
275                })
276                .collect::<EvalResult<Vec<_>>>()?
277        }
278        _ => {
279            return Err(EvalError::Runtime(
280                "fn* expects vector or arity clauses".into(),
281            ));
282        }
283    };
284
285    // Capture closed-over locals.
286    let (closed_over_names, closed_over_vals) = env.all_local_bindings();
287
288    let mut cljrs_fn = CljxFn::new(
289        name.clone(),
290        arities,
291        closed_over_names,
292        closed_over_vals,
293        false,
294        Arc::clone(&env.current_ns),
295    );
296    cljrs_fn.is_async = is_async;
297
298    env.on_fn_defined(&cljrs_fn);
299
300    // Eagerly lower each arity to IR if the compiler is ready.
301    //eager_lower_fn(&cljrs_fn, env);
302
303    let mut ptr = GcPtr::new(cljrs_fn);
304    // For named anonymous functions (fn g ...), store a back-pointer so that
305    // the self-reference returned from the body is pointer-equal to the outer
306    // binding — required for `(= f (f))` to be `true`.
307    if ptr.get().name.is_some() {
308        let self_clone = ptr.clone();
309        ptr.get_mut().self_ptr = Some(self_clone);
310    }
311    Ok(Value::Fn(ptr))
312}
313
314/// Parse one arity: params-form and body forms.
315pub fn parse_arity(params_form: &Form, body: &[Form]) -> EvalResult<CljxFnArity> {
316    let Some(param_forms) = params_form.as_vector() else {
317        return Err(EvalError::Runtime(
318            "fn arity params must be a vector".into(),
319        ));
320    };
321
322    let mut params: Vec<Arc<str>> = Vec::new();
323    let mut param_hints: Vec<Option<TypeHint>> = Vec::new();
324    let mut rest_param: Option<Arc<str>> = None;
325    let mut rest_hint: Option<TypeHint> = None;
326    let mut destructure_params: Vec<(usize, Form)> = Vec::new();
327    let mut destructure_rest: Option<Form> = None;
328    let mut saw_amp = false;
329
330    for p in param_forms {
331        // Peel a leading `^hint` (e.g. `^long x`, `^doubles a`) from the param,
332        // resolving the `:tag` to a primitive `TypeHint`.  Unknown / non-primitive
333        // tags resolve to `None` and are simply ignored (Clojure treats them as
334        // advisory).  The unwrapped form keeps the existing symbol/destructure
335        // handling unchanged.
336        let (hint, p) = peel_param_hint(p);
337        match &p.kind {
338            FormKind::Symbol(s) if s == "&" => {
339                saw_amp = true;
340            }
341            FormKind::Symbol(s) => {
342                if saw_amp {
343                    rest_param = Some(Arc::from(s.as_str()));
344                    rest_hint = hint;
345                    break;
346                } else {
347                    params.push(Arc::from(s.as_str()));
348                    param_hints.push(hint);
349                }
350            }
351            // Destructuring patterns: vectors and maps
352            FormKind::Vector(_) | FormKind::Map(_) => {
353                if saw_amp {
354                    let gensym = format!("__destructure_rest_{}", params.len());
355                    rest_param = Some(Arc::from(gensym.as_str()));
356                    destructure_rest = Some(p.clone());
357                    break;
358                } else {
359                    let idx = params.len();
360                    let gensym = format!("__destructure_{idx}");
361                    params.push(Arc::from(gensym.as_str()));
362                    // Destructured params can't be primitive-tagged.
363                    param_hints.push(None);
364                    destructure_params.push((idx, p.clone()));
365                }
366            }
367            _ => {
368                return Err(EvalError::Runtime(
369                    "fn params must be symbols, vectors, or maps".into(),
370                ));
371            }
372        }
373    }
374
375    let body = desugar_pre_post_conditions(body);
376
377    Ok(CljxFnArity {
378        params,
379        rest_param,
380        body,
381        destructure_params,
382        destructure_rest,
383        ir_arity_id: crate::interp::arity::fresh_arity_id(),
384        param_hints,
385        rest_hint,
386    })
387}
388
389/// Desugar a `:pre`/`:post` conditions map at the head of a function body.
390///
391/// Clojure binds `%` to the return value inside `:post` conditions. This
392/// transforms the raw body forms into equivalent assertion code so the
393/// interpreter does not need to handle conditions separately at call time.
394///
395/// Input body (simplified):
396/// ```text
397/// [{:pre [(pos? x)] :post [(pos? %)]} (inc x)]
398/// ```
399/// Output body:
400/// ```text
401/// [(assert (pos? x))
402///  (let* [% (inc x)]
403///    (assert (pos? %))
404///    %)]
405/// ```
406fn desugar_pre_post_conditions(body: &[Form]) -> Vec<Form> {
407    let first = match body.first() {
408        Some(f) => f,
409        None => return body.to_vec(),
410    };
411
412    let entries = match first.as_map() {
413        Some(entries) => entries,
414        None => return body.to_vec(),
415    };
416
417    let mut pre_conds: Vec<Form> = Vec::new();
418    let mut post_conds: Vec<Form> = Vec::new();
419    let mut has_conditions = false;
420
421    for chunk in entries.chunks(2) {
422        if chunk.len() < 2 {
423            break;
424        }
425        let (key, val) = (&chunk[0], &chunk[1]);
426        match &key.kind {
427            FormKind::Keyword(k) if k == "pre" => {
428                if let FormKind::Vector(conds) = &val.kind {
429                    pre_conds.extend_from_slice(conds);
430                    has_conditions = true;
431                }
432            }
433            FormKind::Keyword(k) if k == "post" => {
434                if let FormKind::Vector(conds) = &val.kind {
435                    post_conds.extend_from_slice(conds);
436                    has_conditions = true;
437                }
438            }
439            _ => {}
440        }
441    }
442
443    if !has_conditions {
444        return body.to_vec();
445    }
446
447    let real_body = &body[1..]; // strip the conditions map
448    let span = first.span.clone();
449    let mut new_body: Vec<Form> = Vec::new();
450
451    // Emit (assert cond) for each :pre condition.
452    for cond in &pre_conds {
453        new_body.push(make_assert_call(cond, &span));
454    }
455
456    if post_conds.is_empty() {
457        new_body.extend_from_slice(real_body);
458    } else {
459        // Wrap real body in (let* [% <body>] (assert post-cond)... %)
460        // so that % refers to the return value inside :post conditions.
461        let percent = Form::new(FormKind::Symbol("%".to_string()), span.clone());
462
463        let body_expr = if real_body.len() == 1 {
464            real_body[0].clone()
465        } else {
466            let mut do_forms = vec![Form::new(FormKind::Symbol("do".to_string()), span.clone())];
467            do_forms.extend_from_slice(real_body);
468            Form::new(FormKind::List(do_forms), span.clone())
469        };
470
471        let binding_vec = Form::new(
472            FormKind::Vector(vec![percent.clone(), body_expr]),
473            span.clone(),
474        );
475
476        let mut let_forms = vec![
477            Form::new(FormKind::Symbol("let*".to_string()), span.clone()),
478            binding_vec,
479        ];
480        for cond in &post_conds {
481            let_forms.push(make_assert_call(cond, &span));
482        }
483        let_forms.push(percent);
484
485        new_body.push(Form::new(FormKind::List(let_forms), span));
486    }
487
488    new_body
489}
490
491/// Build `(assert <cond>)` as a Form node.
492fn make_assert_call(cond: &Form, span: &cljrs_types::span::Span) -> Form {
493    Form::new(
494        FormKind::List(vec![
495            Form::new(FormKind::Symbol("assert".to_string()), span.clone()),
496            cond.clone(),
497        ]),
498        span.clone(),
499    )
500}
501
502/// Peel a `^hint` wrapper off a parameter form, returning the resolved
503/// primitive [`TypeHint`] (if the tag is a recognized primitive) together with
504/// the unwrapped inner form.  A param with no metadata returns `(None, form)`.
505fn peel_param_hint(p: &Form) -> (Option<TypeHint>, &Form) {
506    if let FormKind::Meta(meta, inner) = &p.kind {
507        let hint = tag_name_of_meta(meta).and_then(|n| TypeHint::from_tag(&n));
508        // Recurse in case of stacked metadata; the innermost form is the param.
509        let (inner_hint, inner_form) = peel_param_hint(inner);
510        (hint.or(inner_hint), inner_form)
511    } else {
512        (None, p)
513    }
514}
515
516/// Extract the bare tag name from a `^meta` form: `^long` (symbol shorthand) or
517/// `^{:tag long}` (explicit map).  Strips any namespace from the tag symbol.
518fn tag_name_of_meta(meta: &Form) -> Option<Arc<str>> {
519    match &meta.kind {
520        // `^long x` — the metadata is a bare symbol naming the tag.
521        FormKind::Symbol(s) => Some(strip_tag_ns(s)),
522        // `^{:tag long} x` — pull the `:tag` entry out of the map literal.
523        FormKind::Map(entries) => entries.chunks(2).find_map(|kv| {
524            let is_tag = matches!(&kv[0].kind, FormKind::Keyword(k) if k == "tag");
525            if !is_tag {
526                return None;
527            }
528            match kv.get(1).map(|f| &f.kind) {
529                Some(FormKind::Symbol(s)) => Some(strip_tag_ns(s)),
530                Some(FormKind::Str(s)) => Some(strip_tag_ns(s)),
531                _ => None,
532            }
533        }),
534        _ => None,
535    }
536}
537
538/// Strip a namespace prefix from a tag name (`clojure.core/long` → `long`).
539fn strip_tag_ns(s: &str) -> Arc<str> {
540    match s.rfind('/') {
541        Some(pos) if pos + 1 < s.len() => Arc::from(&s[pos + 1..]),
542        _ => Arc::from(s),
543    }
544}
545
546// ── if ────────────────────────────────────────────────────────────────────────
547
548fn eval_if(args: &[Form], env: &mut Env) -> EvalResult {
549    if args.is_empty() {
550        return Err(EvalError::Runtime("if requires a test".into()));
551    }
552    let test = eval(&args[0], env)?;
553    let truthy = !matches!(test, Value::Nil | Value::Bool(false));
554    if truthy {
555        if args.len() > 1 {
556            eval(&args[1], env)
557        } else {
558            Ok(Value::Nil)
559        }
560    } else if args.len() > 2 {
561        eval(&args[2], env)
562    } else {
563        Ok(Value::Nil)
564    }
565}
566
567// ── let* ──────────────────────────────────────────────────────────────────────
568
569fn eval_let(args: &[Form], env: &mut Env) -> EvalResult {
570    let bindings = match args.first().and_then(|f| f.as_vector()) {
571        Some(v) => expand_pairs(v)
572            .map_err(|_| EvalError::Runtime("let* binding vector must have even length".into()))?
573            .into_owned(),
574        None => return Err(EvalError::Runtime("let* requires a binding vector".into())),
575    };
576
577    let body = &args[1..];
578
579    // Detect assoc/conj chains that can be virtualized.
580    let chains = crate::interp::virtualize::detect_let_chains(&bindings);
581    let virtualizable_chains = find_virtualizable_chains(&chains, &bindings, body);
582
583    env.push_frame();
584
585    let pairs: Vec<_> = bindings.chunks(2).collect();
586    let mut i = 0;
587    while i < pairs.len() {
588        // Check if this binding starts a virtualizable chain.
589        if let Some(chain) = virtualizable_chains.iter().find(|c| c.start == i) {
590            match eval_virtualized_chain(chain, &pairs, env) {
591                Ok(()) => {
592                    i += chain.len;
593                    continue;
594                }
595                Err(e) => {
596                    env.pop_frame();
597                    return Err(e);
598                }
599            }
600        }
601
602        // Normal evaluation.
603        let pair = pairs[i];
604        let val = match eval(&pair[1], env) {
605            Ok(v) => v,
606            Err(e) => {
607                env.pop_frame();
608                return Err(e);
609            }
610        };
611        if let Err(e) = bind_pattern(&pair[0], val, env) {
612            env.pop_frame();
613            return Err(e);
614        }
615        i += 1;
616    }
617
618    let result = eval_body(body, env);
619    env.pop_frame();
620    result
621}
622
623/// Filter chains to only those that are safe to virtualize.
624///
625/// A chain is safe if no intermediate binding is used outside the chain
626/// (i.e., it's only used as the collection argument of the next step).
627fn find_virtualizable_chains<'a>(
628    chains: &'a [crate::interp::virtualize::LetChain],
629    bindings: &[Form],
630    body: &[Form],
631) -> Vec<&'a crate::interp::virtualize::LetChain> {
632    chains
633        .iter()
634        .filter(|chain| {
635            // Check that no intermediate (all except the last) is used in body
636            // or in other bindings outside the chain.
637            for j in chain.start..(chain.start + chain.len - 1) {
638                let name = match &bindings[j * 2].kind {
639                    FormKind::Symbol(s) => s.as_str(),
640                    _ => return false,
641                };
642                if crate::interp::virtualize::binding_used_in_body(name, body) {
643                    return false;
644                }
645                if crate::interp::virtualize::binding_used_in_other_bindings(
646                    name,
647                    bindings,
648                    chain.start,
649                    chain.len,
650                ) {
651                    return false;
652                }
653            }
654            true
655        })
656        .collect()
657}
658
659/// Evaluate an assoc/conj chain using transient operations.
660///
661/// Instead of creating N intermediate persistent collections, we:
662/// 1. Evaluate the root collection (first arg of the first assoc/conj)
663/// 2. Convert to transient
664/// 3. Apply each assoc!/conj! mutably
665/// 4. Convert back to persistent
666/// 5. Bind only the final name (and intermediate names point to intermediate
667///    transient values for correctness, though they shouldn't be used).
668fn eval_virtualized_chain(
669    chain: &crate::interp::virtualize::LetChain,
670    pairs: &[&[Form]],
671    env: &mut Env,
672) -> Result<(), EvalError> {
673    use crate::builtins::transients::{
674        builtin_assoc_bang, builtin_conj_bang, builtin_persistent_bang, builtin_transient,
675    };
676
677    // Step 1: Evaluate the root collection (first arg of the first call).
678    let first_pair = pairs[chain.start];
679    let first_expr_forms = match &first_pair[1].kind {
680        FormKind::List(forms) => forms,
681        _ => unreachable!("chain detection ensures this is a list"),
682    };
683    let root_collection = eval(&first_expr_forms[1], env)?;
684
685    // Step 2: Convert to transient.
686    let mut transient = match builtin_transient(std::slice::from_ref(&root_collection)) {
687        Ok(t) => t,
688        Err(_) => {
689            // Collection doesn't support transients (e.g., sorted map).
690            // Fall back to normal evaluation for the whole chain.
691            return eval_chain_normally(chain, pairs, env);
692        }
693    };
694
695    // Step 3: Apply each chain operation using transient mutation.
696    for j in 0..chain.len {
697        let pair_idx = chain.start + j;
698        let pair = pairs[pair_idx];
699        let expr_forms = match &pair[1].kind {
700            FormKind::List(forms) => forms,
701            _ => unreachable!(),
702        };
703
704        // Evaluate the non-collection arguments.
705        let mut args = vec![transient.clone()];
706        for arg_form in expr_forms.iter().skip(2) {
707            args.push(eval(arg_form, env)?);
708        }
709
710        // Apply the transient operation.
711        transient = match chain.ops[j] {
712            crate::interp::virtualize::ChainOpKind::Assoc => {
713                builtin_assoc_bang(&args).map_err(|e| EvalError::Runtime(e.to_string()))?
714            }
715            crate::interp::virtualize::ChainOpKind::Conj => {
716                builtin_conj_bang(&args).map_err(|e| EvalError::Runtime(e.to_string()))?
717            }
718        };
719
720        // Bind intermediate names to a placeholder (they shouldn't be used,
721        // but we need them in the env for structural correctness).
722        // Only the last binding gets the persistent result.
723        if j < chain.len - 1 {
724            let name = match &pair[0].kind {
725                FormKind::Symbol(s) => s.clone(),
726                _ => unreachable!(),
727            };
728            // Bind to nil as placeholder — intermediates are verified as unused.
729            env.bind(Arc::from(name.as_str()), Value::Nil);
730        }
731    }
732
733    // Step 4: Convert back to persistent.
734    let persistent =
735        builtin_persistent_bang(&[transient]).map_err(|e| EvalError::Runtime(e.to_string()))?;
736
737    // Step 5: Bind the final name.
738    let last_pair = pairs[chain.start + chain.len - 1];
739    bind_pattern(&last_pair[0], persistent, env)?;
740
741    Ok(())
742}
743
744/// Fallback: evaluate a chain using normal persistent operations.
745fn eval_chain_normally(
746    chain: &crate::interp::virtualize::LetChain,
747    pairs: &[&[Form]],
748    env: &mut Env,
749) -> Result<(), EvalError> {
750    for j in 0..chain.len {
751        let pair_idx = chain.start + j;
752        let pair = pairs[pair_idx];
753        let val = eval(&pair[1], env)?;
754        bind_pattern(&pair[0], val, env)?;
755    }
756    Ok(())
757}
758
759// ── loop* / recur ─────────────────────────────────────────────────────────────
760
761pub fn eval_loop(args: &[Form], env: &mut Env) -> EvalResult {
762    let bindings = match args.first().and_then(|f| f.as_vector()) {
763        Some(v) => expand_pairs(v)
764            .map_err(|_| EvalError::Runtime("loop* binding vector must have even length".into()))?
765            .into_owned(),
766        None => return Err(EvalError::Runtime("loop* requires a binding vector".into())),
767    };
768
769    let body = &args[1..];
770
771    // Separate pattern forms and initial values.
772    let patterns: Vec<Form> = bindings.iter().step_by(2).cloned().collect();
773    let mut current_vals: Vec<Value> = Vec::new();
774
775    // Evaluate initial values.
776    for i in (1..bindings.len()).step_by(2) {
777        current_vals.push(eval(&bindings[i], env)?);
778    }
779
780    loop {
781        // Root current_vals so they survive GC — they're not yet bound in env.
782        let _vals_root = crate::env::gc_roots::root_values(&current_vals);
783
784        // GC safepoint on every loop iteration so tight recur loops
785        // don't starve the collector.
786        crate::env::gc_roots::gc_safepoint(env);
787
788        // Under no-gc: push a fresh scratch region for this iteration.
789        // Intermediates allocated in the body land here and are freed
790        // after the iteration ends.
791        #[cfg(feature = "no-gc")]
792        let mut scratch = cljrs_gc::alloc_ctx::ScratchGuard::new();
793
794        // Under GC: scope this iteration's heap allocations in a fresh alloc
795        // frame.  Every value the body allocates is rooted (via ALLOC_ROOTS)
796        // only until this frame drops at the end of the iteration; the
797        // intermediates — and the previous iteration's now-dead recur values —
798        // then become collectable, instead of being pinned for the lifetime of
799        // the enclosing top-level form.  `result` (the return value or recur
800        // values) is moved out before the frame drops and is re-rooted at the
801        // top of the next iteration (`root_values`) or by the caller during
802        // return unwinding; no GC safepoint runs in the interval, exactly as
803        // the IR/JIT dispatch seam relies on (see `crate::tiered::apply`).
804        #[cfg(not(feature = "no-gc"))]
805        let _iter_frame = cljrs_gc::push_alloc_frame();
806
807        env.push_frame();
808        for (pat, val) in patterns.iter().zip(current_vals.iter()) {
809            if let Err(e) = bind_pattern(pat, val.clone(), env) {
810                env.pop_frame();
811                return Err(e);
812            }
813        }
814
815        // Under no-gc: pop scratch before tail expression so the return value
816        // or recur args are allocated in the enclosing scope's context.
817        #[cfg(not(feature = "no-gc"))]
818        let result = eval_body_recur(body, env);
819        #[cfg(feature = "no-gc")]
820        let result = eval_body_with_scratch_loop(body, &mut scratch, env);
821
822        env.pop_frame();
823        // scratch / _iter_frame drop at the end of the iteration (after the
824        // match below), freeing this iteration's intermediates.
825
826        match result {
827            Ok(v) => return Ok(v),
828            Err(EvalError::Recur(new_vals)) => {
829                if new_vals.len() != patterns.len() {
830                    return Err(EvalError::Arity {
831                        name: "recur".into(),
832                        expected: patterns.len().to_string(),
833                        got: new_vals.len(),
834                    });
835                }
836                // new_vals were evaluated in the caller's context (scratch was
837                // popped before the tail form), so they survive the reset.
838                current_vals = new_vals;
839            }
840            Err(e) => return Err(e),
841        }
842    }
843}
844
845fn eval_recur(args: &[Form], env: &mut Env) -> EvalResult {
846    let vals: Vec<Value> = args
847        .iter()
848        .map(|f| eval(f, env))
849        .collect::<EvalResult<_>>()?;
850    Err(EvalError::Recur(vals))
851}
852
853/// Eval body forms, propagating Recur without catching it.
854pub fn eval_body_recur(body: &[Form], env: &mut Env) -> EvalResult {
855    let mut result = Value::Nil;
856    for form in body {
857        result = eval(form, env)?;
858    }
859    Ok(result)
860}
861
862/// Under `no-gc`: eval loop body with scratch-region semantics.
863///
864/// Evaluates all non-tail forms inside the scratch region, then pops the
865/// scratch before the tail (return/recur) expression so it lands in the
866/// caller's context.
867#[cfg(feature = "no-gc")]
868fn eval_body_with_scratch_loop(
869    body: &[Form],
870    scratch: &mut cljrs_gc::alloc_ctx::ScratchGuard,
871    env: &mut Env,
872) -> EvalResult {
873    if body.is_empty() {
874        scratch.pop_for_return();
875        return Ok(Value::Nil);
876    }
877    for form in &body[..body.len() - 1] {
878        eval(form, env)?;
879    }
880    scratch.pop_for_return();
881    eval(&body[body.len() - 1], env)
882}
883
884// ── quote ─────────────────────────────────────────────────────────────────────
885
886fn eval_quote(args: &[Form], env: &Env) -> EvalResult {
887    match args.first() {
888        // `::kw` and an auto-resolved map's symbol keys resolve against the
889        // reading namespace even under quote - the JVM resolves them at read
890        // time, before quote can see them.
891        Some(f) => form_to_value(&resolve_auto_forms(f, env)?),
892        None => Err(EvalError::Runtime("quote requires an argument".into())),
893    }
894}
895
896// ── var ───────────────────────────────────────────────────────────────────────
897
898fn eval_var(args: &[Form], env: &mut Env) -> EvalResult {
899    let sym = match args.first().map(|f| &f.kind) {
900        Some(FormKind::Symbol(s)) => s.clone(),
901        _ => return Err(EvalError::Runtime("var requires a symbol".into())),
902    };
903    let parsed = cljrs_value::Symbol::parse(&sym);
904    let ns: Arc<str> = env.resolve_ns_or_current(parsed.namespace.as_deref());
905    let name = parsed.name.as_ref();
906    env.globals
907        .lookup_var_in_ns(&ns, name)
908        .map(Value::Var)
909        .ok_or_else(|| EvalError::UnboundSymbol(sym))
910}
911
912// ── set! ──────────────────────────────────────────────────────────────────────
913
914fn eval_set_bang(args: &[Form], env: &mut Env) -> EvalResult {
915    let target = args
916        .first()
917        .ok_or_else(|| EvalError::Runtime("set! requires a target".into()))?;
918    let val = if args.len() > 1 {
919        eval(&args[1], env)?
920    } else {
921        Value::Nil
922    };
923    match &target.kind {
924        FormKind::Symbol(sym) => set_bang_symbol(sym, val, env),
925        _ => match set_bang_field_target(target) {
926            Some((field, inst_form)) => {
927                let inst = eval(inst_form, env)?;
928                set_type_instance_field(&inst, field, val)
929            }
930            None => Err(set_bang_target_error()),
931        },
932    }
933}
934
935/// `(set! sym val)` for an already-evaluated `val`: a mutable deftype field in
936/// scope, else the thread-local binding of the var `sym` names, else its root.
937pub fn set_bang_symbol(sym: &str, val: Value, env: &mut Env) -> EvalResult {
938    // A bare unqualified name inside a deftype method may be one of its
939    // mutable fields; only if not does it fall through to var logic.
940    if !sym.contains('/')
941        && let Some(v) = try_set_mutable_field(env, sym, &val)?
942    {
943        return Ok(v);
944    }
945    let parsed = cljrs_value::Symbol::parse(sym);
946    let ns = parsed.namespace.as_deref().unwrap_or(&env.current_ns);
947    let var = env
948        .globals
949        .lookup_var_in_ns(ns, &parsed.name)
950        .ok_or_else(|| EvalError::UnboundSymbol(sym.to_string()))?;
951    // Prefer updating the thread-local binding if one exists.
952    if !crate::env::dynamics::set_thread_local(&var, val.clone()) {
953        var.get().bind(val.clone());
954    }
955    Ok(val)
956}
957
958/// For a `(set! (.-field inst) v)` target, the field name and the `inst`
959/// form; `None` for any other non-symbol target.
960pub fn set_bang_field_target(target: &Form) -> Option<(&str, &Form)> {
961    match &target.kind {
962        FormKind::List(parts) if parts.len() == 2 => match &parts[0].kind {
963            FormKind::Symbol(op) if op.starts_with(".-") => Some((&op[2..], &parts[1])),
964            _ => None,
965        },
966        _ => None,
967    }
968}
969
970/// The error for a `set!` target that is neither a symbol nor `(.-field inst)`.
971pub fn set_bang_target_error() -> EvalError {
972    EvalError::Runtime("set! requires a symbol or (.-field inst) target".into())
973}
974
975/// If `sym` names a mutable field of the `__deftype_self__` instance in scope,
976/// update its cell AND refresh the in-scope local snapshot, returning the new
977/// value. `None` when `sym` is not such a field, so `set!` falls through to var
978/// logic.
979fn try_set_mutable_field(env: &mut Env, sym: &str, val: &Value) -> EvalResult<Option<Value>> {
980    let Some(Value::TypeInstance(ti)) = env.lookup_local_frames(DEFTYPE_SELF) else {
981        return Ok(None);
982    };
983    let Some(atom) = ti.get().mutable.clone() else {
984        return Ok(None);
985    };
986    let key = Value::keyword(cljrs_value::Keyword::simple(sym));
987    let Value::Map(map) = atom.get().deref() else {
988        return Ok(None);
989    };
990    if map.get(&key).is_none() {
991        return Ok(None);
992    }
993    atom.get().reset(Value::Map(map.assoc(key, val.clone())));
994    // Keep the method's local snapshot consistent for later reads.
995    env.bind(Arc::from(sym), val.clone());
996    Ok(Some(val.clone()))
997}
998
999/// Set a mutable field on an explicit instance: `(set! (.-field inst) v)`.
1000pub fn set_type_instance_field(inst: &Value, field: &str, val: Value) -> EvalResult {
1001    let Value::TypeInstance(ti) = inst else {
1002        return Err(EvalError::Runtime(format!(
1003            "set! (.-{field} …): target is not a type instance"
1004        )));
1005    };
1006    let atom = ti.get().mutable.clone().ok_or_else(|| {
1007        EvalError::Runtime(format!("set! (.-{field} …): type has no mutable fields"))
1008    })?;
1009    let key = Value::keyword(cljrs_value::Keyword::simple(field));
1010    let map = match atom.get().deref() {
1011        Value::Map(m) => m,
1012        _ => MapValue::empty(),
1013    };
1014    if map.get(&key).is_none() {
1015        return Err(EvalError::Runtime(format!(
1016            "set!: {field} is not a mutable field"
1017        )));
1018    }
1019    atom.get().reset(Value::Map(map.assoc(key, val.clone())));
1020    Ok(val)
1021}
1022
1023// ── throw ─────────────────────────────────────────────────────────────────────
1024
1025fn eval_throw(args: &[Form], env: &mut Env) -> EvalResult {
1026    let val = match args.first() {
1027        Some(f) => eval(f, env)?,
1028        None => Value::Nil,
1029    };
1030    Err(throw_value(val))
1031}
1032
1033/// The error `(throw val)` raises for an already-evaluated `val`.
1034///
1035/// Wraps non-error values in an ExceptionInfo so try/catch always sees a
1036/// Value::Error and ex-message / ex-data work uniformly inside the handler.
1037pub fn throw_value(val: Value) -> EvalError {
1038    let val = match val {
1039        Value::Error(_) => val,
1040        other => {
1041            let msg = format!("{}", other);
1042            Value::Error(GcPtr::new(ExceptionInfo::new(
1043                ValueError::Other(msg.clone()),
1044                msg,
1045                None,
1046                None,
1047            )))
1048        }
1049    };
1050    EvalError::Thrown(val)
1051}
1052
1053// ── try ───────────────────────────────────────────────────────────────────────
1054
1055/// A catch clause: `(catch Type binding body...)`.
1056///
1057/// Public so the async evaluator (`cljrs-async`) can reuse `parse_try_args` to
1058/// build a yielding `try`/`catch`.
1059pub struct CatchClause<'a> {
1060    pub type_sym: &'a str,
1061    pub binding: &'a str,
1062    pub body: &'a [Form],
1063}
1064
1065/// Convert a non-Thrown EvalError into a `Value::Error` so it can be bound
1066/// inside a catch clause and inspected with `ex-message` / `ex-data`.
1067///
1068/// Public so the async evaluator can convert errors the same way `try` does.
1069pub fn eval_error_to_value(err: &EvalError) -> Value {
1070    let msg = err.to_string();
1071    Value::Error(GcPtr::new(ExceptionInfo::new(
1072        ValueError::Other(msg.clone()),
1073        msg,
1074        None,
1075        None,
1076    )))
1077}
1078
1079/// Test whether the type symbol on a `(catch <Type> e ...)` clause matches a
1080/// thrown value. Type names are matched by their last `.`-separated segment so
1081/// fully-qualified names like `java.lang.Exception` work as well as bare ones.
1082pub fn catch_type_matches(type_name: &str, val: &Value) -> bool {
1083    let short = type_name.rsplit('.').next().unwrap_or(type_name);
1084    match short {
1085        // Catch-all (matches any value, error or not — back-compat).
1086        // `:default` is the ClojureScript universal catch keyword; it arrives
1087        // here as the keyword's name ("default") via `parse_try_args`.
1088        "Object" | "Exception" | "Throwable" | "Error" | "default" => true,
1089        // ExceptionInfo only matches actual ex-info / Exception values.
1090        "ExceptionInfo" => matches!(val, Value::Error(_)),
1091        _ => false,
1092    }
1093}
1094
1095fn eval_try(args: &[Form], env: &mut Env) -> EvalResult {
1096    let (body, catches, fin_body) = parse_try_args(args);
1097
1098    let mut result = eval_body(body, env);
1099
1100    // Handle catch: never intercept Recur (loop trampoline signal).
1101    let err_opt = match std::mem::replace(&mut result, Ok(Value::Nil)) {
1102        Ok(v) => {
1103            result = Ok(v);
1104            None
1105        }
1106        Err(EvalError::Recur(args)) => {
1107            result = Err(EvalError::Recur(args));
1108            None
1109        }
1110        Err(EvalError::GasExhausted) => {
1111            result = Err(EvalError::GasExhausted);
1112            None
1113        }
1114        Err(other) => Some(other),
1115    };
1116
1117    if let Some(err) = err_opt {
1118        let thrown_val = match err {
1119            EvalError::Thrown(v) => v,
1120            ref other => eval_error_to_value(other),
1121        };
1122        let mut handled = false;
1123        for c in &catches {
1124            if catch_type_matches(c.type_sym, &thrown_val) {
1125                env.push_frame();
1126                env.bind(Arc::from(c.binding), thrown_val.clone());
1127                result = eval_body(c.body, env);
1128                env.pop_frame();
1129                handled = true;
1130                break;
1131            }
1132        }
1133        if !handled {
1134            // No matching catch — re-throw.
1135            result = Err(EvalError::Thrown(thrown_val));
1136        }
1137    }
1138
1139    // Always run finally.  Its normal (Ok) value is discarded — `try` returns
1140    // the body/catch result — but an exception thrown from `finally` supersedes
1141    // the pending result or exception (matching JVM/Clojure semantics).
1142    if !fin_body.is_empty() {
1143        eval_body(fin_body, env)?;
1144    }
1145
1146    result
1147}
1148
1149/// The class name or `:default` keyword naming what a `catch` clause catches.
1150fn catch_target(f: &Form) -> Option<&str> {
1151    f.as_symbol().or_else(|| f.as_keyword())
1152}
1153
1154/// Split try args into (body, catch clauses, finally body).
1155///
1156/// Public so the async evaluator can parse `try` forms identically.
1157pub fn parse_try_args(args: &[Form]) -> (&[Form], Vec<CatchClause<'_>>, &[Form]) {
1158    let mut body_end = args.len();
1159    let mut catches: Vec<CatchClause<'_>> = Vec::new();
1160    let mut fin_body: &[Form] = &[];
1161
1162    for (i, form) in args.iter().enumerate() {
1163        if let Some(parts) = form.as_list()
1164            && let Some(s) = parts.first().and_then(|f| f.as_symbol())
1165        {
1166            if s == "catch" {
1167                if i < body_end {
1168                    body_end = i;
1169                }
1170                // The catch type may be a symbol (`Throwable`, `java.lang.Exception`),
1171                // the ClojureScript `:default` catch-all keyword, or a reader
1172                // conditional `#?(:rust Exception :clj ...)` whose selected branch
1173                // resolves to one of those.
1174                let type_sym = match parts.get(1) {
1175                    Some(f) => match catch_target(f) {
1176                        Some(name) => name,
1177                        None => match &f.unmeta().kind {
1178                            FormKind::ReaderCond {
1179                                splicing: false,
1180                                clauses,
1181                            } => match select_reader_cond(clauses).and_then(catch_target) {
1182                                Some(name) => name,
1183                                None => continue,
1184                            },
1185                            _ => continue,
1186                        },
1187                    },
1188                    None => continue,
1189                };
1190                let binding = match parts.get(2).and_then(|f| f.as_symbol()) {
1191                    Some(s) => s,
1192                    None => continue,
1193                };
1194                catches.push(CatchClause {
1195                    type_sym,
1196                    binding,
1197                    body: &parts[3..],
1198                });
1199                continue;
1200            }
1201            if s == "finally" {
1202                if i < body_end {
1203                    body_end = i;
1204                }
1205                fin_body = &parts[1..];
1206                continue;
1207            }
1208        }
1209    }
1210
1211    (&args[..body_end], catches, fin_body)
1212}
1213
1214// ── defn ──────────────────────────────────────────────────────────────────────
1215
1216pub fn eval_defn(args: &[Form], env: &mut Env, private: bool) -> EvalResult {
1217    // The name may carry reader metadata, e.g. `(defn ^:async fetch ...)`.
1218    let name_form = args
1219        .first()
1220        .ok_or_else(|| EvalError::Runtime("defn requires a symbol name".into()))?;
1221    let (name_metas, name_sym) = name_form.peel_meta();
1222    let (name, mut is_async) = match &name_sym.kind {
1223        FormKind::Symbol(s) => (s.clone(), name_metas.iter().any(|m| meta_form_is_async(m))),
1224        _ => return Err(EvalError::Runtime("defn name must be a symbol".into())),
1225    };
1226    // Optional docstring and/or metadata map after the name.
1227    // Valid orderings: (defn name body...), (defn name "doc" body...),
1228    // (defn name {:meta ...} body...), (defn name "doc" {:meta ...} body...).
1229    let mut rest_start = 1;
1230    let mut docstring: Option<String> = None;
1231    if rest_start < args.len()
1232        && let Some(s) = args[rest_start].as_string()
1233    {
1234        docstring = Some(s.to_string());
1235        rest_start += 1;
1236    }
1237    let mut attr_meta: Option<Value> = None;
1238    if rest_start < args.len() && args[rest_start].as_map().is_some() {
1239        // An attr-map such as `{:async true}` can also request async dispatch.
1240        is_async |= meta_form_is_async(&args[rest_start]);
1241        attr_meta = Some(eval(&args[rest_start], env)?);
1242        rest_start += 1;
1243    }
1244    // Build (fn* name ...)
1245    let mut fn_args = vec![Form::new(
1246        FormKind::Symbol(name.to_string()),
1247        args[0].span.clone(),
1248    )];
1249    fn_args.extend_from_slice(&args[rest_start..]);
1250    // Under no-gc: the Fn object must live in the StaticArena since the Var
1251    // intern outlives all scratch regions.
1252    #[cfg(feature = "no-gc")]
1253    let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1254    let mut fn_val = eval_fn(&fn_args, env)?;
1255    if is_async && let Value::Fn(ref mut f) = fn_val {
1256        f.get_mut().is_async = true;
1257    }
1258    let var = env
1259        .globals
1260        .intern(&env.current_ns, Arc::from(name.as_str()), fn_val.clone());
1261    let mut meta = attr_meta;
1262    if let Some(doc) = &docstring {
1263        meta = merge_meta(meta, Some(doc_meta(doc)));
1264    }
1265    meta = merge_meta(meta, arglists_meta(&fn_val, 0));
1266    if private {
1267        meta = merge_meta(meta, Some(private_meta()));
1268    }
1269    if let Some(meta_val) = meta {
1270        var.get().set_meta(meta_val);
1271    }
1272    Ok(Value::Var(var))
1273}
1274
1275// ── defmacro ──────────────────────────────────────────────────────────────────
1276
1277/// Prepend `&form` and `&env` Form symbols to an arity form's parameter vector.
1278///
1279/// Handles:
1280/// - Single-arity vector `[params...]` → `[&form &env params...]`
1281/// - Multi-arity clause list `([params...] body...)` → `([&form &env params...] body...)`
1282fn prepend_macro_params(form: &Form) -> Form {
1283    let span = form.span.clone();
1284    match &form.unmeta().kind {
1285        FormKind::Vector(params) => {
1286            let mut new_params = vec![
1287                Form::new(FormKind::Symbol("&form".to_string()), span.clone()),
1288                Form::new(FormKind::Symbol("&env".to_string()), span.clone()),
1289            ];
1290            new_params.extend_from_slice(params);
1291            Form::new(FormKind::Vector(new_params), span)
1292        }
1293        FormKind::List(forms) => {
1294            // Arity clause: ([params...] body...) — prepend to first element (params vector).
1295            if let Some(first) = forms.first() {
1296                let new_params_form = prepend_macro_params(first);
1297                let mut new_forms = vec![new_params_form];
1298                new_forms.extend_from_slice(&forms[1..]);
1299                Form::new(FormKind::List(new_forms), span)
1300            } else {
1301                form.clone()
1302            }
1303        }
1304        _ => form.clone(),
1305    }
1306}
1307
1308fn eval_defmacro(args: &[Form], env: &mut Env) -> EvalResult {
1309    // The name may carry reader metadata, e.g. `(defmacro ^:private foo ...)`,
1310    // possibly stacked (`^:a ^:b foo`).
1311    let mut current = args
1312        .first()
1313        .ok_or_else(|| EvalError::Runtime("defmacro requires a symbol at position 0".into()))?
1314        .clone();
1315    let mut name_meta: Option<Value> = None;
1316    while let FormKind::Meta(meta_form, inner) = current.kind {
1317        let meta_val = compile_meta_form(&meta_form, env)?;
1318        name_meta = merge_meta(name_meta, Some(meta_val));
1319        current = *inner;
1320    }
1321    let name = match &current.kind {
1322        FormKind::Symbol(s) => s.clone(),
1323        _ => {
1324            return Err(EvalError::Runtime(
1325                "defmacro requires a symbol at position 0".into(),
1326            ));
1327        }
1328    };
1329
1330    let mut rest_start = 1;
1331    let mut docstring: Option<String> = None;
1332    if rest_start < args.len()
1333        && let FormKind::Str(s) = &args[rest_start].kind
1334    {
1335        docstring = Some(s.clone());
1336        rest_start += 1;
1337    }
1338    let mut attr_meta = None;
1339    if rest_start < args.len() && matches!(args[rest_start].kind, FormKind::Map(_)) {
1340        attr_meta = Some(eval(&args[rest_start], env)?);
1341        rest_start += 1;
1342    }
1343    // Prepend implicit &form and &env params to each arity.
1344    let mut fn_args = vec![Form::new(
1345        FormKind::Symbol(name.to_string()),
1346        args[0].span.clone(),
1347    )];
1348    for form in &args[rest_start..] {
1349        fn_args.push(prepend_macro_params(form));
1350    }
1351    // Under no-gc: the Macro object must live in the StaticArena since the Var
1352    // intern outlives all scratch regions.
1353    #[cfg(feature = "no-gc")]
1354    let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1355    let fn_val = eval_fn(&fn_args, env)?;
1356
1357    // Convert Fn → Macro by setting is_macro = true.
1358    let macro_val = match fn_val {
1359        Value::Fn(f) => {
1360            let mut mfn = f.get().clone();
1361            mfn.is_macro = true;
1362            Value::Macro(GcPtr::new(mfn))
1363        }
1364        other => other,
1365    };
1366
1367    let var = env
1368        .globals
1369        .intern(&env.current_ns, Arc::from(name.as_str()), macro_val.clone());
1370    let mut meta = merge_meta(name_meta, attr_meta);
1371    if let Some(doc) = &docstring {
1372        meta = merge_meta(meta, Some(doc_meta(doc)));
1373    }
1374    // Skip the implicit &form/&env params when showing the macro's signature.
1375    meta = merge_meta(meta, arglists_meta(&macro_val, 2));
1376    if let Some(m) = meta {
1377        var.get().set_meta(m);
1378    }
1379    Ok(Value::Var(var))
1380}
1381
1382// ── defonce ───────────────────────────────────────────────────────────────────
1383
1384fn eval_defonce(args: &[Form], env: &mut Env) -> EvalResult {
1385    if args.is_empty() {
1386        return Err(EvalError::Runtime("defonce requires a name".into()));
1387    }
1388    // Share `def`'s name extraction so a metadata-carrying symbol such as
1389    // `(defonce ^:private registry (atom {}))` is accepted here too.
1390    let (name, _meta) = extract_def_name(&args[0], env)?;
1391    // If already bound, return immediately.
1392    if let Some(var) = defonce_existing(&name, env) {
1393        return Ok(var);
1394    }
1395    eval_def(args, env)
1396}
1397
1398// ── and / or ──────────────────────────────────────────────────────────────────
1399
1400fn eval_and(args: &[Form], env: &mut Env) -> EvalResult {
1401    let mut result = Value::Bool(true);
1402    for form in args {
1403        result = eval(form, env)?;
1404        if matches!(result, Value::Nil | Value::Bool(false)) {
1405            return Ok(result);
1406        }
1407    }
1408    Ok(result)
1409}
1410
1411fn eval_or(args: &[Form], env: &mut Env) -> EvalResult {
1412    let mut last = Value::Nil;
1413    for form in args {
1414        last = eval(form, env)?;
1415        if !matches!(last, Value::Nil | Value::Bool(false)) {
1416            return Ok(last);
1417        }
1418    }
1419    Ok(last)
1420}
1421
1422// ── require ───────────────────────────────────────────────────────────────────
1423
1424fn eval_require(args: &[Form], env: &mut Env) -> EvalResult {
1425    for arg in args {
1426        let val = eval(arg, env)?;
1427        let spec = parse_require_spec_val(val).map_err(EvalError::Runtime)?;
1428        load_ns(env.globals.clone(), &spec, &env.current_ns)?;
1429    }
1430    Ok(Value::Nil)
1431}
1432
1433/// Parse a `RequireSpec` from an already-evaluated `Value`.
1434/// Accepts: `'some.ns`, `['some.ns :as alias]`, `['some.ns :refer [syms]]`,
1435/// `['some.ns :refer :all]`, and versioned forms like `'some.ns@abc1234` or
1436/// `['some.ns@abc1234 :as alias]`.
1437fn parse_require_spec_val(val: Value) -> Result<RequireSpec, String> {
1438    match val {
1439        Value::Symbol(s) => {
1440            let sym = s.get();
1441            Ok(RequireSpec {
1442                ns: sym.name.clone(),
1443                version: sym.version.clone(),
1444                alias: None,
1445                refer: RequireRefer::None,
1446            })
1447        }
1448        Value::Vector(v) => {
1449            let items: Vec<Value> = v.get().iter().cloned().collect();
1450            if items.is_empty() {
1451                return Err("require spec vector must not be empty".into());
1452            }
1453            let (ns, version) = match &items[0] {
1454                Value::Symbol(s) => {
1455                    let sym = s.get();
1456                    (sym.name.clone(), sym.version.clone())
1457                }
1458                other => {
1459                    return Err(format!(
1460                        "require spec: first element must be a symbol, got {}",
1461                        other.type_name()
1462                    ));
1463                }
1464            };
1465            let mut alias = None;
1466            let mut refer = RequireRefer::None;
1467            let mut i = 1;
1468            while i < items.len() {
1469                match &items[i] {
1470                    Value::Keyword(k) if k.get().name.as_ref() == "as" => {
1471                        i += 1;
1472                        alias = Some(match items.get(i) {
1473                            Some(Value::Symbol(s)) => s.get().name.clone(),
1474                            _ => return Err("require :as expects a symbol".into()),
1475                        });
1476                    }
1477                    Value::Keyword(k) if k.get().name.as_ref() == "refer" => {
1478                        i += 1;
1479                        refer = match items.get(i) {
1480                            Some(Value::Keyword(k2)) if k2.get().name.as_ref() == "all" => {
1481                                RequireRefer::All
1482                            }
1483                            Some(Value::Vector(rv)) => {
1484                                let names: Vec<Arc<str>> = rv
1485                                    .get()
1486                                    .iter()
1487                                    .map(|v| match v {
1488                                        Value::Symbol(s) => Ok(s.get().name.clone()),
1489                                        other => Err(format!(
1490                                            "require :refer expects symbols, got {}",
1491                                            other.type_name()
1492                                        )),
1493                                    })
1494                                    .collect::<Result<_, _>>()?;
1495                                RequireRefer::Named(names)
1496                            }
1497                            _ => {
1498                                return Err(
1499                                    "require :refer expects :all or a vector of symbols".into()
1500                                );
1501                            }
1502                        };
1503                    }
1504                    other => {
1505                        return Err(format!(
1506                            "require spec: unexpected option {}",
1507                            other.type_name()
1508                        ));
1509                    }
1510                }
1511                i += 1;
1512            }
1513            Ok(RequireSpec {
1514                ns,
1515                version,
1516                alias,
1517                refer,
1518            })
1519        }
1520        other => Err(format!(
1521            "require expects a symbol or vector, got {}",
1522            other.type_name()
1523        )),
1524    }
1525}
1526
1527/// The form a require-spec element denotes: a reader conditional resolves to
1528/// its selected branch, `None` when no branch matches this platform. Any other
1529/// form is itself.
1530fn spec_element(form: &Form) -> Option<&Form> {
1531    match &form.kind {
1532        FormKind::ReaderCond { clauses, .. } => select_reader_cond(clauses),
1533        _ => Some(form),
1534    }
1535}
1536
1537/// Parse a `RequireSpec` from a raw `Form` (unevaluated, used in `ns` macro).
1538/// Also handles versioned namespace symbols such as `my.ns@abc1234`.
1539///
1540/// Public so the AOT compiler can establish an entry namespace from the same
1541/// parse `eval_ns` uses. A second implementation there would drift: it already
1542/// did, dropping `:refer` and `@version` from structurally emitted requires.
1543pub fn parse_require_spec_form(form: &Form) -> Result<RequireSpec, String> {
1544    match &form.kind {
1545        FormKind::Symbol(s) => {
1546            let sym = cljrs_value::Symbol::parse(s);
1547            Ok(RequireSpec {
1548                ns: sym.name.clone(),
1549                version: sym.version.clone(),
1550                alias: None,
1551                refer: RequireRefer::None,
1552            })
1553        }
1554        FormKind::Vector(items) => {
1555            if items.is_empty() {
1556                return Err("require spec vector must not be empty".into());
1557            }
1558            let head = spec_element(&items[0]).ok_or_else(|| {
1559                "require spec: no reader-conditional branch matched for the namespace".to_string()
1560            })?;
1561            let (ns, version) = match &head.kind {
1562                FormKind::Symbol(s) => {
1563                    let sym = cljrs_value::Symbol::parse(s);
1564                    (sym.name.clone(), sym.version.clone())
1565                }
1566                _ => return Err("require spec: first element must be a symbol".into()),
1567            };
1568            let mut alias = None;
1569            let mut refer = RequireRefer::None;
1570            let mut i = 1;
1571            while i < items.len() {
1572                // An option whose conditional selects nothing is dropped; the
1573                // namespace slot above cannot be, so it errors instead.
1574                let Some(item) = spec_element(&items[i]) else {
1575                    i += 1;
1576                    continue;
1577                };
1578                match &item.kind {
1579                    FormKind::Keyword(k) if k == "as" => {
1580                        i += 1;
1581                        alias = Some(match items.get(i).map(|f| &f.kind) {
1582                            Some(FormKind::Symbol(s)) => Arc::from(s.as_str()),
1583                            _ => return Err("require :as expects a symbol".into()),
1584                        });
1585                    }
1586                    FormKind::Keyword(k) if k == "refer" => {
1587                        i += 1;
1588                        refer = match items.get(i).map(|f| &f.kind) {
1589                            Some(FormKind::Keyword(k2)) if k2 == "all" => RequireRefer::All,
1590                            Some(FormKind::Vector(rv)) => {
1591                                let names: Vec<Arc<str>> = rv
1592                                    .iter()
1593                                    .map(|f| match &f.kind {
1594                                        FormKind::Symbol(s) => Ok(Arc::from(s.as_str())),
1595                                        _ => Err("require :refer expects symbols".to_string()),
1596                                    })
1597                                    .collect::<Result<_, _>>()?;
1598                                RequireRefer::Named(names)
1599                            }
1600                            _ => return Err("require :refer expects :all or a vector".into()),
1601                        };
1602                    }
1603                    _ => return Err(format!("require spec: unexpected form at position {i}")),
1604                }
1605                i += 1;
1606            }
1607            Ok(RequireSpec {
1608                ns,
1609                version,
1610                alias,
1611                refer,
1612            })
1613        }
1614        _ => Err("require spec must be a symbol or vector".into()),
1615    }
1616}
1617
1618// ── ns ────────────────────────────────────────────────────────────────────────
1619
1620/// Extract the ns name and optional metadata from the `ns` macro's name form.
1621/// Handles plain symbols and `^meta` shorthand / map forms, e.g.
1622/// `(ns ^{:doc "..."} my.ns ...)` or `(ns ^:no-doc my.ns ...)`.
1623fn extract_ns_name_form(form: Option<&Form>, env: &mut Env) -> EvalResult<(String, Option<Value>)> {
1624    let mut current = form
1625        .ok_or_else(|| EvalError::Runtime("ns requires a symbol at position 0".into()))?
1626        .clone();
1627    let mut meta_acc: Option<Value> = None;
1628    while let FormKind::Meta(meta_form, inner) = current.kind {
1629        let meta_val = compile_meta_form(&meta_form, env)?;
1630        meta_acc = merge_meta(meta_acc, Some(meta_val));
1631        current = *inner;
1632    }
1633    match &current.kind {
1634        FormKind::Symbol(s) => Ok((s.clone(), meta_acc)),
1635        _ => Err(EvalError::Runtime(
1636            "ns requires a symbol at position 0".into(),
1637        )),
1638    }
1639}
1640
1641/// Merge two optional metadata maps, with `overlay` entries taking
1642/// precedence over `base` entries (matching Clojure's `(merge base overlay)`).
1643fn merge_meta(base: Option<Value>, overlay: Option<Value>) -> Option<Value> {
1644    match (base, overlay) {
1645        (None, None) => None,
1646        (Some(b), None) => Some(b),
1647        (None, Some(o)) => Some(o),
1648        (Some(Value::Map(b)), Some(Value::Map(o))) => {
1649            let mut merged = b;
1650            for (k, v) in o.iter() {
1651                merged = merged.assoc(k.clone(), v.clone());
1652            }
1653            Some(Value::Map(merged))
1654        }
1655        (_, Some(o)) => Some(o),
1656    }
1657}
1658
1659/// Parse the body of a `(:refer-clojure ...)` clause into the filter applied
1660/// to the automatic `clojure.core` refer.  Accepts `:exclude`, `:only` and
1661/// `:rename`, each once, in any order.
1662fn parse_refer_clojure_clause(items: &[Form]) -> Result<ReferClojureFilter, String> {
1663    let mut filter = ReferClojureFilter::default();
1664    let items = expand_reader_conds(items);
1665    let mut i = 0;
1666    while i < items.len() {
1667        let FormKind::Keyword(k) = &items[i].kind else {
1668            return Err(":refer-clojure expects keyword options (:exclude, :only, :rename)".into());
1669        };
1670        let arg = items
1671            .get(i + 1)
1672            .ok_or_else(|| format!(":refer-clojure :{k} expects a value"))?;
1673        match k.as_str() {
1674            "exclude" => filter.exclude = symbol_name_set(arg, "exclude")?,
1675            "only" => filter.only = Some(symbol_name_set(arg, "only")?),
1676            "rename" => {
1677                let FormKind::Map(entries) = &arg.kind else {
1678                    return Err(":refer-clojure :rename expects a map".into());
1679                };
1680                if entries.len() % 2 != 0 {
1681                    return Err(":refer-clojure :rename expects an even-sized map".into());
1682                }
1683                for pair in entries.chunks(2) {
1684                    match (&pair[0].kind, &pair[1].kind) {
1685                        (FormKind::Symbol(from), FormKind::Symbol(to)) => {
1686                            filter
1687                                .rename
1688                                .insert(Arc::from(from.as_str()), Arc::from(to.as_str()));
1689                        }
1690                        _ => {
1691                            return Err(
1692                                ":refer-clojure :rename expects symbol keys and values".into()
1693                            );
1694                        }
1695                    }
1696                }
1697            }
1698            other => return Err(format!(":refer-clojure does not support :{other}")),
1699        }
1700        i += 2;
1701    }
1702    Ok(filter)
1703}
1704
1705/// Read a vector of symbols (an `:exclude`/`:only` list) as a set of names.
1706fn symbol_name_set(form: &Form, opt: &str) -> Result<HashSet<Arc<str>>, String> {
1707    let FormKind::Vector(items) = &form.kind else {
1708        return Err(format!(":refer-clojure :{opt} expects a vector of symbols"));
1709    };
1710    expand_reader_conds(items)
1711        .iter()
1712        .map(|f| match &f.kind {
1713            FormKind::Symbol(sym) => Ok(Arc::from(sym.as_str())),
1714            _ => Err(format!(":refer-clojure :{opt} expects symbols")),
1715        })
1716        .collect()
1717}
1718
1719fn eval_ns(args: &[Form], env: &mut Env) -> EvalResult {
1720    let (name, name_meta) = extract_ns_name_form(args.first(), env)?;
1721    env.globals.get_or_create_ns(&name);
1722    env.current_ns = Arc::from(name.as_str());
1723    // Auto-refer clojure.core (Clojure default behaviour).
1724    if name != "clojure.core" {
1725        env.globals.refer_core(&name);
1726    }
1727    sync_star_ns(env);
1728
1729    // Optional docstring, then optional attr-map, before reference clauses.
1730    let mut rest = &args[1..];
1731    if matches!(rest.first().map(|f| &f.kind), Some(FormKind::Str(_))) {
1732        rest = &rest[1..];
1733    }
1734    let mut attr_meta = None;
1735    if matches!(rest.first().map(|f| &f.kind), Some(FormKind::Map(_))) {
1736        attr_meta = Some(eval(&rest[0], env)?);
1737        rest = &rest[1..];
1738    }
1739
1740    if let Some(m) = merge_meta(name_meta, attr_meta) {
1741        let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
1742        ns_ptr.get().set_meta(m);
1743    }
1744
1745    // `:refer-clojure` first: it narrows the automatic core refer done above,
1746    // and an explicit `:require ... :refer` below must be able to override it.
1747    // Re-evaluating an `ns` form without the clause clears a filter left by an
1748    // earlier evaluation, restoring the full core refer.
1749    //
1750    // More than one `:refer-clojure` clause is not meaningful, so the last one
1751    // wins rather than accumulating.  (Clojure runs each clause as a separate
1752    // `refer` call, where a second clause silently *re-adds* what the first
1753    // excluded; there is no reading of that under which both clauses do what
1754    // they say.)
1755    let mut refer_clojure = None;
1756    for clause in rest {
1757        if let FormKind::List(items) = &clause.kind
1758            && matches!(items.first().map(|f| &f.kind), Some(FormKind::Keyword(k)) if k == "refer-clojure")
1759        {
1760            refer_clojure =
1761                Some(parse_refer_clojure_clause(&items[1..]).map_err(EvalError::Runtime)?);
1762        }
1763    }
1764    if name != "clojure.core" {
1765        env.globals
1766            .set_refer_clojure_filter(&name, refer_clojure)
1767            .map_err(EvalError::Runtime)?;
1768    }
1769
1770    for clause in rest {
1771        if let FormKind::List(items) = &clause.kind {
1772            match items.first().map(|f| &f.kind) {
1773                Some(FormKind::Keyword(k)) if k == "require" => {
1774                    // Expand reader conditionals among require specs
1775                    let expanded = expand_reader_conds(&items[1..]);
1776                    for spec_form in &expanded {
1777                        let spec =
1778                            parse_require_spec_form(spec_form).map_err(EvalError::Runtime)?;
1779                        load_ns(env.globals.clone(), &spec, &name)?;
1780                    }
1781                }
1782                // `:refer-clojure` was handled in the pass above; other clauses
1783                // (`:use`, `:import`) — skip for now.
1784                _ => {}
1785            }
1786        }
1787    }
1788
1789    let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
1790    Ok(Value::Namespace(ns_ptr))
1791}
1792
1793// ── load-file ─────────────────────────────────────────────────────────────────
1794
1795fn eval_load_file(args: &[Form], env: &mut Env) -> EvalResult {
1796    if args.is_empty() {
1797        return Err(EvalError::Runtime(
1798            "load-file requires a path argument".into(),
1799        ));
1800    }
1801    let path_val = eval(&args[0], env)?;
1802    let path = match &path_val {
1803        Value::Str(s) => s.get().clone(),
1804        v => {
1805            return Err(EvalError::Runtime(format!(
1806                "load-file: expected string, got {}",
1807                v.type_name()
1808            )));
1809        }
1810    };
1811    let src = std::fs::read_to_string(&path)
1812        .map_err(|e| EvalError::Runtime(format!("load-file: {e}")))?;
1813    let mut parser = cljrs_reader::Parser::new(src, path.clone());
1814    let forms = parser
1815        .parse_all()
1816        .map_err(|e| EvalError::Runtime(format!("load-file parse error: {e}")))?;
1817    let mut result = Value::Nil;
1818    for form in forms {
1819        let _alloc_frame = cljrs_gc::push_alloc_frame();
1820        result = eval(&form, env)?;
1821    }
1822    Ok(result)
1823}
1824
1825// ── letfn ─────────────────────────────────────────────────────────────────────
1826
1827fn eval_letfn(args: &[Form], env: &mut Env) -> EvalResult {
1828    push_letfn_frame(args, env)?;
1829    let result = eval_body(&args[1..], env);
1830    env.pop_frame();
1831    result
1832}
1833
1834/// Push a local frame binding every fn of `(letfn [fns…] body…)`, mutually
1835/// visible. On success the caller evaluates the body and pops the frame; on
1836/// error the frame has already been popped.
1837///
1838/// Public so the async evaluator can run the body with a yielding evaluator.
1839pub fn push_letfn_frame(args: &[Form], env: &mut Env) -> EvalResult<()> {
1840    // (letfn [(f [params] body...) ...] body...)
1841    //
1842    // Three passes, because a closure here captures VALUES, not cells:
1843    // `eval_fn` snapshots `env.all_local_bindings()`, so a name bound after the
1844    // closure was built is invisible to it forever. Binding each fn as it was
1845    // built therefore gave letfn `let`-like sequential scope — a backward
1846    // reference resolved, a forward one or a mutual pair raised "unbound
1847    // symbol", which defeats the only reason letfn exists.
1848    let bindings = match args.first().and_then(|f| f.as_vector()) {
1849        Some(v) => expand_reader_conds_cow(v).into_owned(),
1850        None => return Err(EvalError::Runtime("letfn requires a binding vector".into())),
1851    };
1852
1853    env.push_frame();
1854
1855    // Pass 1: bind every name to nil, so the pass-2 snapshot CONTAINS all of
1856    // them. A capture list cannot grow after the fact; it can only be corrected.
1857    for binding in &bindings {
1858        if let Some(parts) = binding.as_list() {
1859            if parts.is_empty() {
1860                continue;
1861            }
1862            let Some(name) = parts[0].as_symbol() else {
1863                env.pop_frame();
1864                return Err(EvalError::Runtime(
1865                    "letfn binding name must be a symbol".into(),
1866                ));
1867            };
1868            env.bind(Arc::from(name), Value::Nil);
1869        }
1870    }
1871
1872    // Pass 2: build the closures. Each captures the real value of any sibling
1873    // already built and nil for the rest.
1874    let mut built: Vec<(Arc<str>, Value)> = Vec::new();
1875    for binding in &bindings {
1876        if let Some(parts) = binding.as_list() {
1877            if parts.is_empty() {
1878                continue;
1879            }
1880            // parts[0] is the name, so eval_fn sees this as a NAMED fn and wires
1881            // up its self-reference; pass 3 handles every other direction.
1882            let fn_val = match eval_fn(parts, env) {
1883                Ok(v) => v,
1884                Err(e) => {
1885                    env.pop_frame();
1886                    return Err(e);
1887                }
1888            };
1889            let name: Arc<str> = match parts[0].as_symbol() {
1890                Some(s) => Arc::from(s),
1891                None => unreachable!("pass 1 rejected every non-symbol name"),
1892            };
1893            env.bind(Arc::clone(&name), fn_val.clone());
1894            built.push((name, fn_val));
1895        }
1896    }
1897
1898    // Pass 3: replace the nil placeholders each closure captured with the
1899    // sibling it actually names. This is what makes the scope MUTUAL rather
1900    // than sequential, and it necessarily builds a reference cycle between
1901    // co-recursive fns — which is inherent to letfn, not an artifact here.
1902    for (_, fn_val) in &built {
1903        if let Value::Fn(ptr) = fn_val {
1904            let mut ptr = ptr.clone();
1905            let f = ptr.get_mut();
1906            for i in 0..f.closed_over_names.len() {
1907                let captured = f.closed_over_names[i].clone();
1908                if let Some((_, real)) = built.iter().find(|(n, _)| *n == captured) {
1909                    f.closed_over_vals[i] = real.clone();
1910                }
1911            }
1912        }
1913    }
1914
1915    Ok(())
1916}
1917
1918// ── in-ns ─────────────────────────────────────────────────────────────────────
1919
1920fn eval_in_ns(args: &[Form], env: &mut Env) -> EvalResult {
1921    // (in-ns 'foo.bar)
1922    if args.is_empty() {
1923        return Err(EvalError::Runtime("in-ns requires a namespace name".into()));
1924    }
1925    let ns_val = eval(&args[0], env)?;
1926    let ns_name = extract_ns_name(&ns_val)?;
1927    env.globals.get_or_create_ns(&ns_name);
1928    env.globals.refer_core(&ns_name);
1929    env.current_ns = Arc::from(ns_name.as_str());
1930    sync_star_ns(env);
1931    let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
1932    Ok(Value::Namespace(ns_ptr))
1933}
1934
1935// ── alias ─────────────────────────────────────────────────────────────────────
1936
1937fn eval_alias(args: &[Form], env: &mut Env) -> EvalResult {
1938    // (alias 'short 'some.long.ns)
1939    if args.len() < 2 {
1940        return Err(EvalError::Runtime(
1941            "alias requires alias-sym and namespace-sym".into(),
1942        ));
1943    }
1944    let alias_val = eval(&args[0], env)?;
1945    let ns_val = eval(&args[1], env)?;
1946
1947    let alias_name = extract_ns_name(&alias_val)?;
1948    let full_ns = extract_ns_name(&ns_val)?;
1949
1950    let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
1951    let mut aliases = ns_ptr.get().aliases.lock().unwrap();
1952    aliases.insert(Arc::from(alias_name.as_str()), Arc::from(full_ns.as_str()));
1953    Ok(Value::Nil)
1954}
1955
1956/// Extract a namespace-name string from a Value::Symbol, Value::Str, or Value::Keyword.
1957fn extract_ns_name(v: &Value) -> EvalResult<String> {
1958    match v {
1959        Value::Symbol(s) => {
1960            // Use the full name (e.g. "clojure.core").
1961            Ok(s.get().name.as_ref().to_string())
1962        }
1963        Value::Str(s) => Ok(s.get().clone()),
1964        Value::Keyword(k) => Ok(k.get().name.as_ref().to_string()),
1965        other => Err(EvalError::Runtime(format!(
1966            "expected a symbol or string for namespace name, got {}",
1967            other.type_name()
1968        ))),
1969    }
1970}
1971
1972// ── protocol* ─────────────────────────────────────────────────────────────────
1973
1974/// Read a `{:name "m" :min-arity n :variadic b}` map into a `ProtocolMethod`.
1975fn protocol_method_of(spec: &Value) -> EvalResult<ProtocolMethod> {
1976    let Value::Map(m) = spec.unwrap_meta() else {
1977        return Err(EvalError::Runtime(format!(
1978            "protocol* method spec must be a map, got {}",
1979            spec.type_name()
1980        )));
1981    };
1982    let field = |k: &str| m.get(&Value::keyword(Keyword::simple(k)));
1983    let name: Arc<str> = match field("name") {
1984        Some(Value::Str(s)) => Arc::from(s.get().as_str()),
1985        Some(Value::Symbol(s)) => Arc::from(s.get().name.as_ref()),
1986        Some(Value::Keyword(k)) => Arc::from(k.get().name.as_ref()),
1987        _ => {
1988            return Err(EvalError::Runtime(
1989                "protocol* method spec needs a :name".into(),
1990            ));
1991        }
1992    };
1993    let min_arity = match field("min-arity") {
1994        Some(Value::Long(n)) if n >= 0 => n as usize,
1995        _ => 1,
1996    };
1997    let variadic = !matches!(
1998        field("variadic"),
1999        None | Some(Value::Nil) | Some(Value::Bool(false))
2000    );
2001    Ok(ProtocolMethod {
2002        name,
2003        min_arity,
2004        variadic,
2005    })
2006}
2007
2008/// `(protocol* Name method-specs extend-via-metadata?)` — mint a protocol.
2009///
2010/// `method-specs` evaluates to a sequence of `{:name :min-arity :variadic}`
2011/// maps. The protocol is created in the CURRENT namespace, which is the whole
2012/// reason this stays a special form: `Protocol.ns` is what qualifies a method
2013/// name for extend-via-metadata dispatch, and a builtin fn cannot see the
2014/// environment. The protocol is returned, not interned — `defprotocol` is the
2015/// Clojure macro that `def`s it along with a dispatch fn per method.
2016fn eval_protocol_star(args: &[Form], env: &mut Env) -> EvalResult {
2017    if args.len() < 2 {
2018        return Err(EvalError::Runtime(
2019            "protocol* requires a name and method specs".into(),
2020        ));
2021    }
2022    // Name metadata belongs on the var `defprotocol` binds, not here.
2023    let (name, _) = require_sym_meta(args, 0, "protocol*", env)?;
2024    let specs = crate::interp::eval::eval(&args[1], env)?;
2025    let specs: Vec<Value> = match specs.unwrap_meta() {
2026        Value::Vector(v) => v.get().iter().cloned().collect(),
2027        Value::Nil => Vec::new(),
2028        v => {
2029            return Err(EvalError::Runtime(format!(
2030                "protocol* method specs must be a vector, got {}",
2031                v.type_name()
2032            )));
2033        }
2034    };
2035    let methods = specs
2036        .iter()
2037        .map(protocol_method_of)
2038        .collect::<EvalResult<Vec<_>>>()?;
2039    let extend_via_metadata = match args.get(2) {
2040        Some(f) => !matches!(
2041            crate::interp::eval::eval(f, env)?,
2042            Value::Nil | Value::Bool(false)
2043        ),
2044        None => false,
2045    };
2046    let proto = Protocol::new(
2047        Arc::from(name.as_str()),
2048        env.current_ns.clone(),
2049        methods,
2050        extend_via_metadata,
2051    );
2052    Ok(Value::Protocol(GcPtr::new(proto)))
2053}
2054
2055/// Build a `CljxFn` from the tail of a method-impl list: `(name [params] body...)`.
2056/// `parts[0]` is the method name symbol (ignored here — caller handles it).
2057/// `parts[1]` is the params vector.
2058/// `parts[2..]` is the body.
2059/// Bring a defrecord's FIELDS into scope in a method body, as
2060/// `(let* [f (:f this) ...] body...)`.
2061///
2062/// The bare field symbol is the idiomatic form — `(mutable? [_] (valid-sha? sha))`
2063/// — and it read as an unbound symbol, because a method impl is built as an
2064/// ordinary fn whose only bindings are its own params. Clojure compiles the
2065/// fields as instance fields of the generated class, so they are simply in
2066/// scope.
2067///
2068/// A field whose name a PARAM already takes is skipped: the param shadows the
2069/// field in Clojure, and binding it here would shadow the param instead.
2070/// Returns None when there is nothing to bind, or when the first param is not
2071/// a plain symbol (a destructured `this` has no name to read the fields from).
2072fn synth_field_scope(
2073    params_form: &Form,
2074    fields: &[Arc<str>],
2075    mutable_fields: &[Arc<str>],
2076    body: &[Form],
2077) -> Option<Vec<Form>> {
2078    let param_forms = match &params_form.kind {
2079        FormKind::Vector(v) => v,
2080        _ => return None,
2081    };
2082    let this_name = match param_forms.first().map(|f| &f.kind) {
2083        Some(FormKind::Symbol(s)) => s.clone(),
2084        _ => return None,
2085    };
2086    let param_names: Vec<&str> = param_forms
2087        .iter()
2088        .filter_map(|f| match &f.kind {
2089            FormKind::Symbol(s) => Some(s.as_str()),
2090            _ => None,
2091        })
2092        .collect();
2093
2094    let span = params_form.span.clone();
2095    let is_mut = |name: &str| mutable_fields.iter().any(|m| m.as_ref() == name);
2096    let mut bindings: Vec<Form> = Vec::new();
2097    for field in fields {
2098        if param_names.contains(&field.as_ref()) {
2099            continue;
2100        }
2101        bindings.push(Form::new(FormKind::Symbol(field.to_string()), span.clone()));
2102        // A mutable field reads through `(.-field this)` — the live cell; an
2103        // immutable one through `(:field this)` — the field map. Both snapshot
2104        // at method entry; `set!` refreshes the local for later reads.
2105        let accessor = if is_mut(field) {
2106            FormKind::List(vec![
2107                Form::new(FormKind::Symbol(format!(".-{field}")), span.clone()),
2108                Form::new(FormKind::Symbol(this_name.clone()), span.clone()),
2109            ])
2110        } else {
2111            FormKind::List(vec![
2112                Form::new(FormKind::Keyword(field.to_string()), span.clone()),
2113                Form::new(FormKind::Symbol(this_name.clone()), span.clone()),
2114            ])
2115        };
2116        bindings.push(Form::new(accessor, span.clone()));
2117    }
2118    // With mutable fields present, bind a hidden handle to `this` so `set!` can
2119    // find the instance whose cell to update.
2120    if !mutable_fields.is_empty() {
2121        bindings.push(Form::new(
2122            FormKind::Symbol(DEFTYPE_SELF.to_string()),
2123            span.clone(),
2124        ));
2125        bindings.push(Form::new(FormKind::Symbol(this_name.clone()), span.clone()));
2126    }
2127    if bindings.is_empty() {
2128        return None;
2129    }
2130
2131    let mut let_forms = vec![
2132        Form::new(FormKind::Symbol("let*".to_string()), span.clone()),
2133        Form::new(FormKind::Vector(bindings), span.clone()),
2134    ];
2135    let_forms.extend_from_slice(body);
2136    Some(vec![Form::new(FormKind::List(let_forms), span)])
2137}
2138
2139fn build_impl_fn(
2140    parts: &[Form],
2141    fields: &[Arc<str>],
2142    mutable_fields: &[Arc<str>],
2143    env: &mut Env,
2144) -> EvalResult<Value> {
2145    if parts.len() < 2 {
2146        return Err(EvalError::Runtime(
2147            "protocol method impl requires params and body".into(),
2148        ));
2149    }
2150    // parts[1] should be the params vector.
2151    let params_form = &parts[1];
2152    let body = &parts[2..];
2153    let scoped;
2154    let body: &[Form] = if fields.is_empty() {
2155        body
2156    } else {
2157        match synth_field_scope(params_form, fields, mutable_fields, body) {
2158            Some(v) => {
2159                scoped = v;
2160                &scoped
2161            }
2162            None => body,
2163        }
2164    };
2165    let arity = parse_arity(params_form, body)?;
2166    let (closed_over_names, closed_over_vals) = env.all_local_bindings();
2167    let fn_name: Option<Arc<str>> = parts[0].as_symbol().map(Arc::from);
2168    let cljrs_fn = CljxFn::new(
2169        fn_name,
2170        vec![arity],
2171        closed_over_names,
2172        closed_over_vals,
2173        false,
2174        Arc::clone(&env.current_ns),
2175    );
2176    Ok(Value::Fn(GcPtr::new(cljrs_fn)))
2177}
2178
2179// ── binding ───────────────────────────────────────────────────────────────────
2180
2181fn eval_binding(args: &[Form], env: &mut Env) -> EvalResult {
2182    let pairs = match args.first().and_then(|f| f.as_vector()) {
2183        Some(v) => expand_pairs(v)
2184            .map_err(|_| EvalError::Runtime("binding vector must have even count".into()))?
2185            .into_owned(),
2186        None => return Err(EvalError::Runtime("binding requires a vector".into())),
2187    };
2188
2189    let mut frame: HashMap<usize, Value> = HashMap::new();
2190    for pair in pairs.chunks(2) {
2191        let Some(sym_str) = pair[0].as_symbol() else {
2192            return Err(EvalError::Runtime("binding targets must be symbols".into()));
2193        };
2194        let parsed = cljrs_value::Symbol::parse(sym_str);
2195        let ns_part: Arc<str> = env.resolve_ns_or_current(parsed.namespace.as_deref());
2196        let var_ptr = env
2197            .globals
2198            .lookup_var_in_ns(&ns_part, &parsed.name)
2199            .ok_or_else(|| EvalError::UnboundSymbol(sym_str.to_string()))?;
2200        let val = eval(&pair[1], env)?;
2201        frame.insert(crate::env::dynamics::var_key_of(&var_ptr), val);
2202    }
2203
2204    let _guard = crate::env::dynamics::push_frame(frame);
2205    eval_body(&args[1..], env)
2206    // _guard drops here → pop_frame()
2207}
2208
2209// ── deftype / defrecord shared construction ─────────────────────────────────────
2210
2211/// Hidden `let*` binding a `deftype` method body carries when the type has
2212/// mutable fields: a handle to `this`, so `set!` can locate the instance whose
2213/// interior-mutable cell to update.
2214const DEFTYPE_SELF: &str = "__deftype_self__";
2215
2216/// Does a field's `^meta` mark it `^:unsynchronized-mutable` or
2217/// `^:volatile-mutable`? Instances are single-threaded here, so the two are
2218/// treated identically — only whether the field is mutable at all matters.
2219fn meta_form_is_mutable(meta: &Form) -> bool {
2220    let is_mut_kw = |k: &str| k == "unsynchronized-mutable" || k == "volatile-mutable";
2221    match &meta.kind {
2222        FormKind::Keyword(k) => is_mut_kw(k),
2223        FormKind::Map(entries) => entries.chunks(2).any(|kv| {
2224            matches!(&kv[0].kind, FormKind::Keyword(k) if is_mut_kw(k))
2225                && !matches!(
2226                    kv.get(1).map(|f| &f.kind),
2227                    None | Some(FormKind::Bool(false)) | Some(FormKind::Nil)
2228                )
2229        }),
2230        // Metadata that reached this form through a macro is QUOTED: it is
2231        // already a value, and re-analysing it would resolve its contents as
2232        // code (see `value_to_form`). A field vector written in source arrives
2233        // unquoted; one a macro emitted arrives quoted. Both describe the same
2234        // field, so both have to be read.
2235        FormKind::Quote(inner) => meta_form_is_mutable(inner),
2236        _ => false,
2237    }
2238}
2239
2240/// A single field spec: its name and whether it is mutable.
2241fn field_spec_of(form: &Form) -> Option<(Arc<str>, bool)> {
2242    match &form.kind {
2243        FormKind::Symbol(s) => Some((Arc::from(s.as_str()), false)),
2244        FormKind::Meta(meta, inner) => {
2245            let here = meta_form_is_mutable(meta);
2246            field_spec_of(inner).map(|(name, inner_mut)| (name, inner_mut || here))
2247        }
2248        _ => None,
2249    }
2250}
2251
2252/// Parse a `deftype` `[field ...]` vector into `(name, mutable?)` specs.
2253fn parse_field_specs(form: &Form, ctx: &str) -> EvalResult<Vec<(Arc<str>, bool)>> {
2254    // `as_vector` reports the shape under any `^meta`, so a marker on the
2255    // vector itself — `(defrecord R ^:marker [x])` — stays transparent.
2256    let Some(fields) = form.as_vector() else {
2257        return Err(EvalError::Runtime(format!(
2258            "{ctx} requires a field vector as second arg"
2259        )));
2260    };
2261    fields
2262        .iter()
2263        .map(|f| {
2264            field_spec_of(f)
2265                .ok_or_else(|| EvalError::Runtime(format!("{ctx} field names must be symbols")))
2266        })
2267        .collect()
2268}
2269
2270// ── deftype* (defrecord / deftype are bootstrap macros over it) ─────────────────
2271
2272/// The irreducible datatype primitive: mint a type tag and register method impls
2273/// against it, with the fields in scope in each body and mutable fields backed by
2274/// live cells. It does NOT synthesise constructors or intern the type symbol —
2275/// that sugar lives in the `deftype` bootstrap macro (`->T`, `(def T 'T)`), which
2276/// is what makes deftype a particular case of a Clojure macro over this form.
2277fn eval_deftype_star(args: &[Form], env: &mut Env) -> EvalResult {
2278    // (deftype* TypeName [field ...] Proto (method [this] body) ...)
2279    if args.len() < 2 {
2280        return Err(EvalError::Runtime(
2281            "deftype* requires a name and field vector".into(),
2282        ));
2283    }
2284    // Type metadata (e.g. ^:private) has no var to hold it; unwrapped so the
2285    // name reads, and deliberately not applied anywhere it would not belong.
2286    let (type_name, _) = require_sym_meta(args, 0, "deftype*", env)?;
2287    let type_tag: Arc<str> = Arc::from(type_name.as_str());
2288
2289    let specs = parse_field_specs(&args[1], "deftype*")?;
2290    let field_names: Vec<Arc<str>> = specs.iter().map(|(n, _)| n.clone()).collect();
2291    let mutable_names: Vec<Arc<str>> = specs
2292        .iter()
2293        .filter(|(_, m)| *m)
2294        .map(|(n, _)| n.clone())
2295        .collect();
2296
2297    // Register protocol/interface method impls, with the fields in scope in
2298    // each body — same machinery as defrecord/reify. Mutable fields read
2299    // through the live cell and are writable with `set!`.
2300    register_impls_for_tag(&type_tag, &args[2..], &field_names, &mutable_names, env)?;
2301    // Return the minted tag so a caller (e.g. the `reify` macro) can feed it
2302    // straight to `make-type-instance` — a single dataflow source for the tag,
2303    // rather than a second textual reference that a gensym could desync.
2304    Ok(Value::string(type_name))
2305}
2306
2307// ── register_impls_for_tag ────────────────────────────────────────────────────
2308
2309/// Resolve a protocol NAME symbol in an impl position (extend-type, extend-protocol,
2310/// reify/defrecord), honouring the current namespace's `:require :as` aliases and
2311/// fully-qualified names — not just an unqualified lookup in the current ns.
2312///
2313/// `(defrecord R [] mp/IThing (-do [_] ...))` failed with "mp/IThing is not a
2314/// protocol" even though the protocol was loaded and (resolve 'mini.proto/IThing)
2315/// was truthy: the old code looked up the whole string "mp/IThing" as an intern of
2316/// the CURRENT ns, where it is neither interned nor referred. A qualified protocol
2317/// symbol must resolve through its own namespace, exactly as `eval` resolves any
2318/// other qualified symbol.
2319fn resolve_protocol_sym(env: &Env, s: &str) -> Option<GcPtr<Protocol>> {
2320    let parsed = cljrs_value::Symbol::parse(s);
2321    let val = match parsed.namespace.as_deref() {
2322        Some(ns_part) => {
2323            let ns = env.resolve_ns_part(ns_part);
2324            env.globals.lookup_in_ns(&ns, &parsed.name)
2325        }
2326        None => env.globals.lookup_in_ns(&env.current_ns, s),
2327    };
2328    match val {
2329        Some(Value::Protocol(p)) => Some(p),
2330        _ => None,
2331    }
2332}
2333
2334/// Parse `Proto (method [params] body) ...` segments and register them under `type_tag`.
2335/// Shared by `defrecord` and `reify`.
2336fn register_impls_for_tag(
2337    type_tag: &Arc<str>,
2338    forms: &[Form],
2339    fields: &[Arc<str>],
2340    mutable_fields: &[Arc<str>],
2341    env: &mut Env,
2342) -> EvalResult<()> {
2343    let mut current_proto: Option<GcPtr<cljrs_value::Protocol>> = None;
2344
2345    for form in forms {
2346        match &form.unmeta().kind {
2347            FormKind::Symbol(s) => match resolve_protocol_sym(env, s) {
2348                Some(p) => current_proto = Some(p),
2349                None => {
2350                    return Err(EvalError::Runtime(format!(
2351                        "reify/defrecord: {} is not a protocol",
2352                        s
2353                    )));
2354                }
2355            },
2356            FormKind::List(parts) => {
2357                let proto = current_proto.as_ref().ok_or_else(|| {
2358                    EvalError::Runtime("reify/defrecord: method impl before protocol name".into())
2359                })?;
2360                if parts.is_empty() {
2361                    continue;
2362                }
2363                let method_name: Arc<str> = match parts[0].as_symbol() {
2364                    Some(s) => Arc::from(s),
2365                    None => continue,
2366                };
2367                let fn_val = build_impl_fn(parts, fields, mutable_fields, env)?;
2368                let mut impls = proto.get().impls.lock().unwrap();
2369                impls
2370                    .entry(type_tag.clone())
2371                    .or_default()
2372                    .insert(method_name, fn_val);
2373                drop(impls);
2374                cljrs_value::bump_protocol_generation();
2375            }
2376            _ => {}
2377        }
2378    }
2379    Ok(())
2380}
2381
2382// ── helpers ───────────────────────────────────────────────────────────────────
2383
2384/// Update the root binding of `*ns*` in `clojure.core` to the current namespace.
2385/// Called whenever `env.current_ns` changes (ns, in-ns, standard_env setup).
2386pub fn sync_star_ns(env: &mut Env) {
2387    if let Some(star_ns_var) = env.globals.lookup_var("clojure.core", "*ns*") {
2388        let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
2389        star_ns_var.get().bind(Value::Namespace(ns_ptr));
2390    }
2391}
2392
2393/// The symbol at `idx`, unwrapping any `^meta` wrapper, together with the
2394/// metadata it carried.
2395///
2396/// `(defprotocol ^:private Driver ...)` reads as `Meta(:private, Symbol("Driver"))`,
2397/// so matching `FormKind::Symbol` alone rejects a form Clojure accepts. malli's
2398/// `malli.impl.regex` opens with five such protocols, which made all of malli
2399/// unloadable.
2400///
2401/// The metadata is RETURNED rather than discarded: dropping it would trade a
2402/// loud error for a silent loss of `^:private`, and callers that intern a var
2403/// attach it there.
2404fn require_sym_meta(
2405    args: &[Form],
2406    idx: usize,
2407    form_name: &str,
2408    env: &mut Env,
2409) -> EvalResult<(String, Option<Value>)> {
2410    fn peel(form: &Form, env: &mut Env) -> EvalResult<Option<(String, Option<Value>)>> {
2411        match &form.kind {
2412            FormKind::Symbol(s) => Ok(Some((s.clone(), None))),
2413            // `^:a ^:b x` nests Meta forms; unwrap all, outer mark winning.
2414            FormKind::Meta(meta_form, inner) => {
2415                let meta_val = compile_meta_form(meta_form, env)?;
2416                match peel(inner, env)? {
2417                    Some((name, inner_meta)) => {
2418                        Ok(Some((name, merge_meta(inner_meta, Some(meta_val)))))
2419                    }
2420                    None => Ok(None),
2421                }
2422            }
2423            _ => Ok(None),
2424        }
2425    }
2426    let err = || EvalError::Runtime(format!("{form_name} requires a symbol at position {idx}"));
2427    match args.get(idx) {
2428        Some(form) => peel(form, env)?.ok_or_else(err),
2429        None => Err(err()),
2430    }
2431}
2432
2433// ── with-out-str ──────────────────────────────────────────────────────────────
2434
2435fn eval_with_out_str(body: &[Form], env: &mut Env) -> EvalResult {
2436    crate::builtins::builtins::push_output_capture();
2437    let result = eval_body(body, env);
2438    let captured = crate::builtins::builtins::pop_output_capture().unwrap_or_default();
2439    // Propagate errors but still pop the capture buffer
2440    result?;
2441    Ok(Value::string(captured))
2442}
2443
2444// ── await ─────────────────────────────────────────────────────────────────────
2445
2446/// Blocking deref in sync context; yielding deref in async context.
2447///
2448/// When `cljrs-async` is loaded, `eval_async` intercepts `await` forms before
2449/// the sync evaluator reaches this handler, so this path is only taken in
2450/// non-async (sync) code. It blocks the OS thread until the future/promise
2451/// resolves — equivalent to `(deref val)`.
2452fn eval_await(args: &[Form], env: &mut Env) -> EvalResult {
2453    if args.is_empty() {
2454        return Err(EvalError::Runtime("await requires one argument".into()));
2455    }
2456    let val = eval(&args[0], env)?;
2457    match val {
2458        Value::Future(f) => {
2459            let mut guard = f.get().state.lock().unwrap();
2460            loop {
2461                match &*guard {
2462                    FutureState::Done(v) => {
2463                        f.get().mark_observed();
2464                        return Ok(v.clone());
2465                    }
2466                    FutureState::Failed(v) => {
2467                        f.get().mark_observed();
2468                        return Err(EvalError::Thrown(v.clone()));
2469                    }
2470                    FutureState::GasExhausted => {
2471                        f.get().mark_observed();
2472                        return Err(EvalError::GasExhausted);
2473                    }
2474                    FutureState::Cancelled => {
2475                        return Err(EvalError::Thrown(CljxFuture::cancelled_error()));
2476                    }
2477                    FutureState::Running => {
2478                        guard = f.get().cond.wait(guard).unwrap();
2479                    }
2480                }
2481            }
2482        }
2483        Value::Promise(p) => Ok(p.get().deref_blocking()),
2484        other => Ok(other),
2485    }
2486}