rukalang/elab/
normalize_calls.rs

1use super::prelude::*;
2
3/// Normalize runtime call arguments by expanding tuple spread operands.
4///
5/// Input invariant: `args` belong to one runtime call expression.
6/// Output invariant: returned arguments contain no `CallArg::Spread` items.
7///
8/// This pass rewrites `...tuple_ident` arguments into indexed field accesses
9/// (`tuple_ident.0`, `tuple_ident.1`, ...), preserving call argument order.
10/// Spread operands must be identifier expressions and must elaborate to tuple
11/// or unit types.
12///
13/// Pass name: `elab.normalize_runtime_calls_and_spreads`.
14struct NormalizeRuntimeCallsAndSpreadsPass<'a> {
15    elaborator: &'a mut Elaborator,
16    locals: &'a mut Vec<(String, Ty)>,
17}
18
19impl<'a> Pass for NormalizeRuntimeCallsAndSpreadsPass<'a> {
20    type In = Vec<CallArg>;
21    type Out = Vec<CallArg>;
22    type Error = ElabError;
23
24    const NAME: &'static str = "elab.normalize_runtime_calls_and_spreads";
25
26    fn run(&mut self, input: Self::In, _cx: &mut PassContext) -> Result<Self::Out, Self::Error> {
27        let mut expanded = Vec::new();
28        for arg in input {
29            match arg {
30                CallArg::Expr(expr) => expanded.push(CallArg::Expr(expr)),
31                CallArg::Type(ty) => expanded.push(CallArg::Type(ty)),
32                CallArg::Spread(mut expr) => {
33                    let Expr::Ident { .. } = &expr else {
34                        return Err(ElabError::InvalidCallSpreadOperand);
35                    };
36                    let tuple_ty = self
37                        .elaborator
38                        .elaborate_expr(&mut expr, None, self.locals)?;
39                    match tuple_ty {
40                        Ty::Unit => {}
41                        Ty::Tuple(items) => {
42                            for index in 0..items.len() {
43                                expanded.push(CallArg::Expr(Expr::Field {
44                                    base: Box::new(expr.clone()),
45                                    field: index.to_string(),
46                                }));
47                            }
48                        }
49                        actual => return Err(ElabError::CallSpreadRequiresTuple { actual }),
50                    }
51                }
52            }
53        }
54        Ok(expanded)
55    }
56}
57
58impl Elaborator {
59    /// Run call/spread normalization for one runtime call-site argument list.
60    pub(super) fn normalize_runtime_calls_and_spreads(
61        &mut self,
62        args: &mut Vec<CallArg>,
63        locals: &mut Vec<(String, Ty)>,
64    ) -> Result<(), ElabError> {
65        let mut pass = NormalizeRuntimeCallsAndSpreadsPass {
66            elaborator: self,
67            locals,
68        };
69        let (output, timings) = Elaborator::run_subpass(&mut pass, std::mem::take(args))?;
70        *args = output;
71        self.record_subpass_timings(&timings);
72        Ok(())
73    }
74}