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::common::{
20
    DoLoop, infer_result_pair_element, infer_wasm_type, parse_do_vars, parse_end_clause,
21
    static_loop_terminates,
22
};
23
use super::runtime::{
24
    compile_do_runtime, compile_do_runtime_for_effect, compile_do_runtime_for_stack,
25
};
26

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

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

            
52
1992
    let mut needs_runtime = init_results.iter().any(Expr::is_wasm_runtime);
53

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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