1
//! `DO` (parallel-step variant) compile + eval handlers.
2
//!
3
//! The three compile entry points (`compile_do`, `_for_effect`,
4
//! `_for_stack`) each handle the static-fold path inline; if the
5
//! end-test or any init is runtime-typed, they hand off to the
6
//! corresponding `runtime::compile_do_runtime{,_for_effect,_for_stack}`.
7
//! `do_form` is the eval-only handler for value-position use.
8

            
9
use crate::ast::{Expr, PairElement, WasmType};
10
use crate::compiler::context::CompileContext;
11
use crate::compiler::emit::FunctionEmitter;
12
use crate::compiler::expr::{
13
    classify_stack_type, compile_body, compile_for_effect, compile_nil, eval_value,
14
};
15
use crate::error::{Error, Result};
16
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
17

            
18
use super::super::binding::eval_body;
19
use super::super::control::is_truthy;
20
use super::common::{
21
    DoLoop, infer_result_pair_element, infer_wasm_type, parse_do_vars, parse_end_clause,
22
    static_loop_terminates,
23
};
24
use super::runtime::{
25
    compile_do_runtime, compile_do_runtime_for_effect, compile_do_runtime_for_stack,
26
};
27

            
28
1988
pub(super) fn compile_do(
29
1988
    ctx: &mut CompileContext,
30
1988
    emit: &mut FunctionEmitter,
31
1988
    symbols: &mut SymbolTable,
32
1988
    args: &[Expr],
33
1988
) -> Result<()> {
34
1988
    if args.len() < 2 {
35
284
        return Err(Error::Compile(
36
284
            "DO requires a variable list and an end clause".to_string(),
37
284
        ));
38
1704
    }
39
1704
    let vars = parse_do_vars("DO", &args[0])?;
40
1704
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
41
1704
    let end_test = end_test.clone();
42
1704
    let result_forms: Vec<Expr> = result_forms.to_vec();
43
1704
    let body = &args[2..];
44

            
45
1704
    let init_results: Vec<Expr> = vars
46
1704
        .iter()
47
2485
        .map(|v| match &v.init {
48
2414
            Some(expr) => eval_value(symbols, expr),
49
71
            None => Ok(Expr::Nil),
50
2485
        })
51
1704
        .collect::<Result<_>>()?;
52

            
53
1704
    let mut needs_runtime = init_results.iter().any(Expr::is_wasm_runtime);
54

            
55
1704
    if !needs_runtime {
56
1633
        let mut trial = symbols.clone();
57
2343
        for (v, val) in vars.iter().zip(&init_results) {
58
2343
            trial.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
59
2343
        }
60
1633
        let test_result = eval_value(&mut trial, &end_test)?;
61
1633
        needs_runtime = test_result.is_wasm_runtime();
62
71
    }
63

            
64
1704
    if needs_runtime {
65
852
        let dl = DoLoop {
66
852
            vars: &vars,
67
852
            end_test: &end_test,
68
852
            result_forms: &result_forms,
69
852
            body,
70
852
            sequential: false,
71
852
        };
72
852
        return compile_do_runtime(ctx, emit, symbols, &dl);
73
852
    }
74

            
75
852
    let mut local = symbols.clone();
76
852
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
77
1207
    for (v, val) in vars.iter().zip(&init_results) {
78
1207
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
79
1207
        stepped.push((v.name.clone(), v.step.clone()));
80
1207
    }
81

            
82
852
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
83
213
        let dl = DoLoop {
84
213
            vars: &vars,
85
213
            end_test: &end_test,
86
213
            result_forms: &result_forms,
87
213
            body,
88
213
            sequential: false,
89
213
        };
90
213
        return compile_do_runtime(ctx, emit, symbols, &dl);
91
639
    }
92

            
93
    loop {
94
2556
        let test = eval_value(&mut local, &end_test)?;
95
2556
        if is_truthy(&test) {
96
639
            return if result_forms.is_empty() {
97
71
                compile_nil(ctx, emit);
98
71
                Ok(())
99
            } else {
100
568
                compile_body(ctx, emit, &mut local, &result_forms)
101
            };
102
1917
        }
103
1917
        for expr in body {
104
            compile_for_effect(ctx, emit, &mut local, expr)?;
105
        }
106
1917
        let new_values: Vec<(&str, Expr)> = stepped
107
1917
            .iter()
108
2485
            .filter_map(|(name, step)| {
109
2485
                step.as_ref()
110
2485
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
111
2485
            })
112
1917
            .collect::<Result<_>>()?;
113
2272
        for (name, val) in new_values {
114
2272
            local
115
2272
                .lookup_mut(name)
116
2272
                .expect("DO variable must exist")
117
2272
                .set_value(val);
118
2272
        }
119
    }
120
1988
}
121

            
122
2485
pub(super) fn compile_do_for_stack(
123
2485
    ctx: &mut CompileContext,
124
2485
    emit: &mut FunctionEmitter,
125
2485
    symbols: &mut SymbolTable,
126
2485
    args: &[Expr],
127
2485
) -> Result<WasmType> {
128
2485
    if args.len() < 2 {
129
        return Err(Error::Compile(
130
            "DO requires a variable list and an end clause".to_string(),
131
        ));
132
2485
    }
133
2485
    let vars = parse_do_vars("DO", &args[0])?;
134
2485
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
135
2485
    let end_test = end_test.clone();
136
2485
    let result_forms: Vec<Expr> = result_forms.to_vec();
137
2485
    let body = &args[2..];
138
2485
    let dl = DoLoop {
139
2485
        vars: &vars,
140
2485
        end_test: &end_test,
141
2485
        result_forms: &result_forms,
142
2485
        body,
143
2485
        sequential: false,
144
2485
    };
145
2485
    compile_do_runtime_for_stack(ctx, emit, symbols, &dl)
146
2485
}
147

            
148
284
pub(super) fn compile_do_for_effect(
149
284
    ctx: &mut CompileContext,
150
284
    emit: &mut FunctionEmitter,
151
284
    symbols: &mut SymbolTable,
152
284
    args: &[Expr],
153
284
) -> Result<()> {
154
284
    if args.len() < 2 {
155
        return Err(Error::Compile(
156
            "DO requires a variable list and an end clause".to_string(),
157
        ));
158
284
    }
159
284
    let vars = parse_do_vars("DO", &args[0])?;
160
284
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
161
284
    let end_test = end_test.clone();
162
284
    let result_forms: Vec<Expr> = result_forms.to_vec();
163
284
    let body = &args[2..];
164

            
165
284
    let init_results: Vec<Expr> = vars
166
284
        .iter()
167
284
        .map(|v| match &v.init {
168
284
            Some(expr) => eval_value(symbols, expr),
169
            None => Ok(Expr::Nil),
170
284
        })
171
284
        .collect::<Result<_>>()?;
172

            
173
284
    let mut needs_runtime = init_results.iter().any(Expr::is_wasm_runtime);
174
284
    if !needs_runtime {
175
284
        let mut trial = symbols.clone();
176
284
        for (v, val) in vars.iter().zip(&init_results) {
177
284
            trial.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
178
284
        }
179
284
        let test_result = eval_value(&mut trial, &end_test)?;
180
284
        needs_runtime = test_result.is_wasm_runtime();
181
    }
182

            
183
284
    if needs_runtime {
184
        let dl = DoLoop {
185
            vars: &vars,
186
            end_test: &end_test,
187
            result_forms: &result_forms,
188
            body,
189
            sequential: false,
190
        };
191
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
192
284
    }
193

            
194
284
    let mut local = symbols.clone();
195
284
    for (v, val) in vars.iter().zip(&init_results) {
196
284
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
197
284
    }
198
284
    let stepped: Vec<(String, Option<Expr>)> = vars
199
284
        .iter()
200
284
        .map(|v| (v.name.clone(), v.step.clone()))
201
284
        .collect();
202

            
203
284
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
204
        let dl = DoLoop {
205
            vars: &vars,
206
            end_test: &end_test,
207
            result_forms: &result_forms,
208
            body,
209
            sequential: false,
210
        };
211
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
212
284
    }
213

            
214
    loop {
215
923
        let test = eval_value(&mut local, &end_test)?;
216
923
        if is_truthy(&test) {
217
284
            for expr in &result_forms {
218
71
                compile_for_effect(ctx, emit, &mut local, expr)?;
219
            }
220
284
            return Ok(());
221
639
        }
222
639
        for expr in body {
223
426
            compile_for_effect(ctx, emit, &mut local, expr)?;
224
        }
225
639
        let new_values: Vec<(&str, Expr)> = stepped
226
639
            .iter()
227
639
            .filter_map(|(name, step)| {
228
639
                step.as_ref()
229
639
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
230
639
            })
231
639
            .collect::<Result<_>>()?;
232
639
        for (name, val) in new_values {
233
639
            local
234
639
                .lookup_mut(name)
235
639
                .expect("DO variable must exist")
236
639
                .set_value(val);
237
639
        }
238
    }
239
284
}
240

            
241
/// The runtime type a DO loop yields, mirroring `compile_do_runtime_for_stack`
242
/// (which returns `compile_for_stack` of the LAST result form, or `Bool`/nil
243
/// when there is none). A result form the body grows via
244
/// `(setf acc (cons V acc))` is a list whose element comes from the cons car
245
/// (`infer_result_pair_element`); any other form — a counter, a do var — takes
246
/// its own resolved runtime type. Hard-coding `PairRef` mistyped a counting
247
/// loop `(do … (end n) (setf n (+ n 1)))` as a pair, so `(= (count …) 0)`
248
/// rejected it as "= expects numeric arguments, got pair".
249
4335
pub(super) fn runtime_result_type(
250
4335
    result_forms: &[Expr],
251
4335
    body: &[Expr],
252
4335
    stepped: &[(String, Option<Expr>)],
253
4335
    infer_env: &SymbolTable,
254
4335
) -> WasmType {
255
4335
    let Some(last) = result_forms.last() else {
256
        return WasmType::Bool;
257
    };
258
4335
    if let Some(elem) = infer_result_pair_element(last, body, stepped, infer_env) {
259
3056
        return WasmType::PairRef(elem);
260
1279
    }
261
    // Resolve the result form against the runtime-typed loop env, then type it
262
    // the way codegen's `compile_for_stack` would: a runtime value reports its
263
    // own `wasm_type`; a const numeric literal goes through `classify_stack_type`
264
    // (an integer counter → I32, matching the let-promoted local codegen emits).
265
1279
    let resolved = eval_value(&mut infer_env.clone(), last).unwrap_or(Expr::Nil);
266
1279
    resolved
267
1279
        .wasm_type()
268
1279
        .or_else(|| classify_stack_type(&resolved))
269
1279
        .unwrap_or(WasmType::PairRef(PairElement::I32))
270
4335
}
271

            
272
4191
pub(super) fn do_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
273
4191
    if args.len() < 2 {
274
71
        return Err(Error::Compile(
275
71
            "DO requires a variable list and an end clause".to_string(),
276
71
        ));
277
4120
    }
278
4120
    let vars = parse_do_vars("DO", &args[0])?;
279
4120
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
280
4120
    let end_test = end_test.clone();
281
4120
    let result_forms: Vec<Expr> = result_forms.to_vec();
282
4120
    let body = &args[2..];
283

            
284
4120
    let init_values: Vec<(String, Expr, Option<Expr>)> = vars
285
4120
        .into_iter()
286
4830
        .map(|v| {
287
4830
            let val = match v.init {
288
4830
                Some(expr) => eval_value(symbols, &expr)?,
289
                None => Expr::Nil,
290
            };
291
4830
            Ok((v.name, val, v.step))
292
4830
        })
293
4120
        .collect::<Result<_>>()?;
294

            
295
4830
    let needs_runtime = init_values.iter().any(|(_, v, _)| v.is_wasm_runtime());
296

            
297
4120
    let mut local = symbols.clone();
298
4120
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
299
4830
    for (name, val, step) in &init_values {
300
4830
        local.define(Symbol::new(name, SymbolKind::Variable).with_value(val.clone()));
301
4830
        stepped.push((name.clone(), step.clone()));
302
4830
    }
303

            
304
    // Compute the static return type. When a result-form names a single
305
    // accumulator that the body builds via `(setf acc (cons V acc))`,
306
    // we peek at the CONS car's type so the static type matches the
307
    // PairElement the codegen path actually emits. Without this, the
308
    // hardcoded PairRef(I32) disagrees with the codegen (which
309
    // allocates the accumulator's element from the car's WasmType),
310
    // and consumer dolists end up downcasting to the wrong element
311
    // shape — a runtime cast failure.
312
    // Resolve the accumulator's element type against an env where each DO var
313
    // is bound as its RUNTIME type, not its const init. The codegen loop var is
314
    // runtime (an integer init `(i 0 …)` makes `i` a runtime Index/I32), so a
315
    // `(cons i acc)` cell is an I32 cell; resolving `i` to the const `0` would
316
    // type it as Ratio (the numeric-literal cell slot) and a consumer dolist
317
    // would downcast to the wrong element → a runtime cast trap.
318
4120
    let mut infer_env = symbols.clone();
319
4830
    for (name, val, step) in &init_values {
320
4830
        let ty = infer_wasm_type(val, step.as_ref(), &infer_env);
321
4830
        infer_env.define(Symbol::new(name, SymbolKind::Variable).with_value(Expr::WasmRuntime(ty)));
322
4830
    }
323
4120
    let result_ty = runtime_result_type(&result_forms, body, &stepped, &infer_env);
324

            
325
4120
    if needs_runtime {
326
        return Ok(Expr::WasmRuntime(result_ty));
327
    } else {
328
4120
        let test_result = eval_value(&mut local, &end_test)?;
329
4120
        if test_result.is_wasm_runtime() {
330
3339
            return Ok(Expr::WasmRuntime(result_ty));
331
781
        }
332
    }
333

            
334
781
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
335
        return Ok(Expr::WasmRuntime(result_ty));
336
781
    }
337

            
338
    loop {
339
2982
        let test = eval_value(&mut local, &end_test)?;
340
2982
        if is_truthy(&test) {
341
781
            return if result_forms.is_empty() {
342
                Ok(Expr::Nil)
343
            } else {
344
781
                eval_body(&mut local, &result_forms)
345
            };
346
2201
        }
347
2201
        for expr in body {
348
            eval_value(&mut local, expr)?;
349
        }
350
2201
        let new_values: Vec<(&str, Expr)> = stepped
351
2201
            .iter()
352
3976
            .filter_map(|(name, step)| {
353
3976
                step.as_ref()
354
3976
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
355
3976
            })
356
2201
            .collect::<Result<_>>()?;
357
3976
        for (name, val) in new_values {
358
3976
            local
359
3976
                .lookup_mut(name)
360
3976
                .expect("DO variable must exist")
361
3976
                .set_value(val);
362
3976
        }
363
    }
364
4191
}