Lines
87.8 %
Functions
33.33 %
Branches
100 %
//! `LIST` — multi-arg list constructor. Lowers to a `$pair` chain when any
//! argument is a runtime value (so the result rides a `$pair` chain rather
//! than being held as a compile-time `Expr::List`) through the CONS cell
//! builders DIRECTLY — never by synthesizing a `(CONS …)` form, which would
//! route through ordinary symbol dispatch and could hit a user
//! `(defun cons …)` shadow.
use crate::ast::{Expr, WasmType};
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::expr::{compile_expr, eval_value, serialize_stack_to_output};
use crate::error::Result;
use crate::runtime::SymbolTable;
use super::cons::compile_pair_chain;
use super::infer::chain_pair_element;
pub(super) fn list(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
let resolved: Vec<Expr> = args
.iter()
.map(|a| eval_value(symbols, a))
.collect::<Result<_>>()?;
// A runtime element can't ride a quoted compile-time list: consumers
// (CAR/CDR/…) would extract a bare `WasmRuntime` placeholder with no real
// stack value and emit invalid wasm. Report the chain `compile_pair_chain`
// emits, typed from the ALREADY-RESOLVED elements so their side effects
// stay single-applied.
if resolved.iter().any(Expr::is_wasm_runtime) {
return Ok(Expr::WasmRuntime(WasmType::PairRef(chain_pair_element(
&resolved,
)?)));
}
Ok(Expr::quoted_list(resolved))
pub(super) fn compile_list(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
// Classify runtime-ness on a CLONE so the live symbol table is mutated by
// exactly ONE resolve pass — the chosen branch below. (Calling `list()` on
// the live table here, then `compile_pair_chain` again, would resolve the
// element expressions twice on the live table.)
if any_runtime_arg(&mut symbols.clone(), args)? {
let ty = compile_list_to_stack(ctx, emit, symbols, args)?;
serialize_stack_to_output(ctx, emit, ty)?;
return Ok(());
let folded = list(symbols, args)?;
compile_expr(ctx, emit, symbols, &folded)
fn any_runtime_arg(symbols: &mut SymbolTable, args: &[Expr]) -> Result<bool> {
for arg in args {
if eval_value(symbols, arg)?.is_wasm_runtime() {
return Ok(true);
Ok(false)
/// Emit `(list …)` as a `$pair` chain. Resolves each element ONCE, then folds
/// right-to-left through `compile_cons_to_stack` — building each pair as a
/// two-element arg slice `[car, <inner-chain>]` whose cdr is the already-built
/// inner chain expression. Calls the CONS builder directly (no `(CONS …)`
/// form, hence no symbol-dispatch / shadow surface) and threads the resolved
/// car values so element side effects are applied exactly once.
pub(super) fn compile_list_to_stack(
) -> Result<WasmType> {
compile_pair_chain(ctx, emit, symbols, args)