rune/compile/ir/
interpreter.rs

1use crate::alloc::prelude::*;
2use crate::alloc::{try_format, Vec};
3use crate::ast::Spanned;
4use crate::compile::ir;
5use crate::compile::ir::scopes::MissingLocal;
6use crate::compile::meta;
7use crate::compile::{self, IrErrorKind, ItemId, ModId, WithSpan};
8use crate::hir;
9use crate::query::{Query, Used};
10use crate::runtime::{self, ConstValue, Object, OwnedTuple, Repr, Value};
11use crate::TypeHash;
12
13/// The interpreter that executed [Ir][crate::ir::Ir].
14pub struct Interpreter<'a, 'arena> {
15    /// A budget associated with the compiler, for how many expressions it's
16    /// allowed to evaluate.
17    pub(crate) budget: Budget,
18    /// The module in which the interpreter is run.
19    pub(crate) module: ModId,
20    /// The item where the constant expression is located.
21    pub(crate) item: ItemId,
22    /// Constant scopes.
23    pub(crate) scopes: ir::Scopes,
24    /// Query engine to look for constant expressions.
25    pub(crate) q: Query<'a, 'arena>,
26}
27
28impl Interpreter<'_, '_> {
29    /// Outer evaluation for an expression which performs caching into `consts`.
30    pub(crate) fn eval_const(&mut self, ir: &ir::Ir, used: Used) -> compile::Result<ConstValue> {
31        tracing::trace!("processing constant: {}", self.q.pool.item(self.item));
32
33        if let Some(const_value) = self.q.consts.get(self.item) {
34            return Ok(const_value.try_clone()?);
35        }
36
37        if !self.q.consts.mark(self.item)? {
38            return Err(compile::Error::new(ir, IrErrorKind::ConstCycle));
39        }
40
41        let ir_value = match ir::eval_ir(ir, self, used) {
42            Ok(ir_value) => ir_value,
43            Err(outcome) => match outcome {
44                ir::EvalOutcome::Error(error) => {
45                    return Err(error);
46                }
47                ir::EvalOutcome::NotConst(span) => {
48                    return Err(compile::Error::new(span, IrErrorKind::NotConst))
49                }
50                ir::EvalOutcome::Break(span, _, _) => {
51                    return Err(compile::Error::new(span, IrErrorKind::BreakOutsideOfLoop))
52                }
53            },
54        };
55
56        let const_value: ConstValue = crate::from_value(ir_value).with_span(ir)?;
57
58        if self
59            .q
60            .consts
61            .insert(self.item, const_value.try_clone()?)?
62            .is_some()
63        {
64            return Err(compile::Error::new(ir, IrErrorKind::ConstCycle));
65        }
66
67        Ok(const_value)
68    }
69
70    /// Evaluate to an ir value.
71    pub(crate) fn eval_value(&mut self, ir: &ir::Ir, used: Used) -> compile::Result<Value> {
72        match ir::eval_ir(ir, self, used) {
73            Ok(ir_value) => Ok(ir_value),
74            Err(outcome) => match outcome {
75                ir::EvalOutcome::Error(error) => Err(error),
76                ir::EvalOutcome::NotConst(span) => {
77                    Err(compile::Error::new(span, IrErrorKind::NotConst))
78                }
79                ir::EvalOutcome::Break(span, _, _) => {
80                    Err(compile::Error::new(span, IrErrorKind::BreakOutsideOfLoop))
81                }
82            },
83        }
84    }
85
86    /// Resolve the given constant value from the block scope.
87    ///
88    /// This looks up `const <ident> = <expr>` and evaluates them while caching
89    /// their result.
90    pub(crate) fn resolve_var(
91        &mut self,
92        span: &dyn Spanned,
93        name: &hir::Variable,
94        used: Used,
95    ) -> compile::Result<Value> {
96        if let Some(ir_value) = self.scopes.try_get(name) {
97            return Ok(ir_value.try_clone()?);
98        }
99
100        let mut base = self.q.pool.item(self.item).try_to_owned()?;
101
102        loop {
103            let item = self
104                .q
105                .pool
106                .alloc_item(base.extended(name.try_to_string()?)?)?;
107
108            if let Some(const_value) = self.q.consts.get(item) {
109                return Ok(const_value.to_value_with(self.q.context).with_span(span)?);
110            }
111
112            if let Some(meta) = self.q.query_meta(span, item, used)? {
113                match &meta.kind {
114                    meta::Kind::Const => {
115                        let Some(const_value) = self.q.get_const_value(meta.hash) else {
116                            return Err(compile::Error::msg(
117                                span,
118                                try_format!("Missing constant for hash {}", meta.hash),
119                            ));
120                        };
121
122                        return Ok(const_value.to_value_with(self.q.context).with_span(span)?);
123                    }
124                    _ => {
125                        return Err(compile::Error::new(
126                            span,
127                            IrErrorKind::UnsupportedMeta {
128                                meta: meta.info(self.q.pool)?,
129                            },
130                        ));
131                    }
132                }
133            }
134
135            if !base.pop() {
136                break;
137            }
138        }
139
140        Err(compile::Error::new(
141            span,
142            MissingLocal(name.try_to_string()?.try_into_boxed_str()?),
143        ))
144    }
145
146    pub(crate) fn call_const_fn<S>(
147        &mut self,
148        spanned: S,
149        id: ItemId,
150        args: Vec<Value>,
151        used: Used,
152    ) -> compile::Result<Value>
153    where
154        S: Copy + Spanned,
155    {
156        let span = Spanned::span(&spanned);
157        let const_fn = self.q.const_fn_for(id).with_span(span)?;
158
159        if const_fn.ir_fn.args.len() != args.len() {
160            return Err(compile::Error::new(
161                span,
162                IrErrorKind::ArgumentCountMismatch {
163                    actual: args.len(),
164                    expected: const_fn.ir_fn.args.len(),
165                },
166            ));
167        }
168
169        let guard = self.scopes.isolate()?;
170
171        for (name, value) in const_fn.ir_fn.args.iter().zip(args) {
172            self.scopes.decl(*name, value).with_span(span)?;
173        }
174
175        let value = self.eval_value(&const_fn.ir_fn.ir, used)?;
176        self.scopes.pop(guard).with_span(span)?;
177        Ok(value)
178    }
179}
180
181impl ir::Scopes {
182    /// Get the given target as mut.
183    pub(crate) fn get_target(&self, ir_target: &ir::IrTarget) -> compile::Result<Value> {
184        match &ir_target.kind {
185            ir::IrTargetKind::Name(name) => Ok(self.get_name(name, ir_target)?.clone()),
186            ir::IrTargetKind::Field(ir_target, field) => {
187                let value = self.get_target(ir_target)?;
188                let object = value.borrow_ref::<Object>().with_span(ir_target)?;
189
190                let Some(value) = object.get(field.as_ref()) else {
191                    return Err(compile::Error::new(
192                        ir_target,
193                        IrErrorKind::MissingField {
194                            field: field.try_clone()?,
195                        },
196                    ));
197                };
198
199                Ok(value.clone())
200            }
201            ir::IrTargetKind::Index(target, index) => {
202                let value = self.get_target(target)?;
203
204                match value.type_hash() {
205                    runtime::Vec::HASH => {
206                        let vec = value.borrow_ref::<runtime::Vec>().with_span(ir_target)?;
207
208                        if let Some(value) = vec.get(*index) {
209                            return Ok(value.clone());
210                        }
211                    }
212                    runtime::OwnedTuple::HASH => {
213                        let tuple = value
214                            .borrow_ref::<runtime::OwnedTuple>()
215                            .with_span(ir_target)?;
216
217                        if let Some(value) = tuple.get(*index) {
218                            return Ok(value.clone());
219                        }
220                    }
221                    _ => {
222                        return Err(compile::Error::expected_type::<OwnedTuple>(
223                            ir_target,
224                            value.type_info(),
225                        ));
226                    }
227                }
228
229                Err(compile::Error::new(
230                    ir_target,
231                    IrErrorKind::MissingIndex { index: *index },
232                ))
233            }
234        }
235    }
236
237    /// Update the given target with the given constant value.
238    pub(crate) fn set_target(
239        &mut self,
240        ir_target: &ir::IrTarget,
241        value: Value,
242    ) -> compile::Result<()> {
243        match &ir_target.kind {
244            ir::IrTargetKind::Name(name) => {
245                *self.get_name_mut(name, ir_target)? = value;
246                Ok(())
247            }
248            ir::IrTargetKind::Field(target, field) => {
249                let target = self.get_target(target)?;
250                let mut object = target.borrow_mut::<Object>().with_span(ir_target)?;
251                let field = field.as_ref().try_to_owned()?;
252                object.insert(field, value).with_span(ir_target)?;
253                Ok(())
254            }
255            ir::IrTargetKind::Index(target, index) => {
256                let target = self.get_target(target)?;
257
258                match target.as_ref() {
259                    Repr::Inline(value) => {
260                        return Err(compile::Error::expected_type::<OwnedTuple>(
261                            ir_target,
262                            value.type_info(),
263                        ));
264                    }
265                    Repr::Dynamic(value) => {
266                        return Err(compile::Error::expected_type::<OwnedTuple>(
267                            ir_target,
268                            value.type_info(),
269                        ));
270                    }
271                    Repr::Any(any) => match any.type_hash() {
272                        runtime::Vec::HASH => {
273                            let mut vec = any.borrow_mut::<runtime::Vec>().with_span(ir_target)?;
274
275                            if let Some(current) = vec.get_mut(*index) {
276                                *current = value;
277                                return Ok(());
278                            }
279                        }
280                        runtime::OwnedTuple::HASH => {
281                            let mut tuple = any
282                                .borrow_mut::<runtime::OwnedTuple>()
283                                .with_span(ir_target)?;
284
285                            if let Some(current) = tuple.get_mut(*index) {
286                                *current = value;
287                                return Ok(());
288                            }
289                        }
290                        _ => {
291                            return Err(compile::Error::expected_type::<OwnedTuple>(
292                                ir_target,
293                                any.type_info(),
294                            ));
295                        }
296                    },
297                };
298
299                Err(compile::Error::msg(ir_target, "missing index"))
300            }
301        }
302    }
303
304    /// Mutate the given target with the given constant value.
305    pub(crate) fn mut_target(
306        &mut self,
307        ir_target: &ir::IrTarget,
308        op: impl FnOnce(&mut Value) -> compile::Result<()>,
309    ) -> compile::Result<()> {
310        match &ir_target.kind {
311            ir::IrTargetKind::Name(name) => {
312                let value = self.get_name_mut(name, ir_target)?;
313                op(value)
314            }
315            ir::IrTargetKind::Field(target, field) => {
316                let value = self.get_target(target)?;
317                let mut object = value.borrow_mut::<Object>().with_span(ir_target)?;
318
319                let Some(value) = object.get_mut(field.as_ref()) else {
320                    return Err(compile::Error::new(
321                        ir_target,
322                        IrErrorKind::MissingField {
323                            field: field.try_clone()?,
324                        },
325                    ));
326                };
327
328                op(value)
329            }
330            ir::IrTargetKind::Index(target, index) => {
331                let current = self.get_target(target)?;
332
333                match current.as_ref() {
334                    Repr::Dynamic(value) => Err(compile::Error::expected_type::<OwnedTuple>(
335                        ir_target,
336                        value.type_info(),
337                    )),
338                    Repr::Any(value) => match value.type_hash() {
339                        runtime::Vec::HASH => {
340                            let mut vec =
341                                value.borrow_mut::<runtime::Vec>().with_span(ir_target)?;
342
343                            let value = vec.get_mut(*index).ok_or_else(|| {
344                                compile::Error::new(
345                                    ir_target,
346                                    IrErrorKind::MissingIndex { index: *index },
347                                )
348                            })?;
349
350                            op(value)
351                        }
352                        runtime::OwnedTuple::HASH => {
353                            let mut tuple = value
354                                .borrow_mut::<runtime::OwnedTuple>()
355                                .with_span(ir_target)?;
356
357                            let value = tuple.get_mut(*index).ok_or_else(|| {
358                                compile::Error::new(
359                                    ir_target,
360                                    IrErrorKind::MissingIndex { index: *index },
361                                )
362                            })?;
363
364                            op(value)
365                        }
366                        _ => Err(compile::Error::expected_type::<OwnedTuple>(
367                            ir_target,
368                            value.type_info(),
369                        )),
370                    },
371                    actual => Err(compile::Error::expected_type::<OwnedTuple>(
372                        ir_target,
373                        actual.type_info(),
374                    )),
375                }
376            }
377        }
378    }
379}
380
381/// A budget dictating the number of evaluations the compiler is allowed to do.
382pub(crate) struct Budget {
383    budget: usize,
384}
385
386impl Budget {
387    /// Construct a new constant evaluation budget with the given constraint.
388    pub(crate) fn new(budget: usize) -> Self {
389        Self { budget }
390    }
391
392    /// Take an item from the budget. Errors if the budget is exceeded.
393    pub(crate) fn take<S>(&mut self, spanned: S) -> compile::Result<()>
394    where
395        S: Spanned,
396    {
397        if self.budget == 0 {
398            return Err(compile::Error::new(spanned, IrErrorKind::BudgetExceeded));
399        }
400
401        self.budget -= 1;
402        Ok(())
403    }
404}