leo_interpreter/
cursor.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::*;
18
19use leo_ast::{
20    ArrayType,
21    AssertVariant,
22    AsyncExpression,
23    BinaryOperation,
24    Block,
25    CoreConstant,
26    CoreFunction,
27    DefinitionPlace,
28    Expression,
29    Function,
30    Location,
31    NodeID,
32    Statement,
33    StructVariableInitializer,
34    Type,
35    UnaryOperation,
36    Variant,
37    interpreter_value::{
38        AsyncExecution,
39        CoreFunctionHelper,
40        Value,
41        evaluate_binary,
42        evaluate_core_function,
43        evaluate_unary,
44        literal_to_value,
45    },
46};
47use leo_errors::{InterpreterHalt, Result};
48use leo_span::{Span, Symbol, sym};
49
50use snarkvm::prelude::{
51    Address,
52    CanaryV0,
53    Closure as SvmClosure,
54    Finalize as SvmFinalize,
55    Function as SvmFunctionParam,
56    MainnetV0,
57    Network,
58    PrivateKey,
59    ProgramID,
60    TestnetV0,
61};
62
63use indexmap::IndexMap;
64use itertools::Itertools;
65use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
66use std::{cmp::Ordering, collections::HashMap, mem, str::FromStr as _};
67
68pub type Closure = SvmClosure<TestnetV0>;
69pub type Finalize = SvmFinalize<TestnetV0>;
70pub type SvmFunction = SvmFunctionParam<TestnetV0>;
71
72/// Names associated to values in a function being executed.
73#[derive(Clone, Debug)]
74pub struct FunctionContext {
75    path: Vec<Symbol>,
76    program: Symbol,
77    pub caller: Value,
78    names: HashMap<Vec<Symbol>, Value>,
79    accumulated_futures: Vec<AsyncExecution>,
80    is_async: bool,
81}
82
83/// A stack of contexts, building with the function call stack.
84#[derive(Clone, Debug, Default)]
85pub struct ContextStack {
86    contexts: Vec<FunctionContext>,
87    current_len: usize,
88}
89
90impl ContextStack {
91    fn len(&self) -> usize {
92        self.current_len
93    }
94
95    fn push(
96        &mut self,
97        path: &[Symbol],
98        program: Symbol,
99        caller: Value,
100        is_async: bool,
101        names: HashMap<Vec<Symbol>, Value>, // a map of variable names that are already known
102    ) {
103        if self.current_len == self.contexts.len() {
104            self.contexts.push(FunctionContext {
105                path: path.to_vec(),
106                program,
107                caller: caller.clone(),
108                names: HashMap::new(),
109                accumulated_futures: Default::default(),
110                is_async,
111            });
112        }
113
114        self.contexts[self.current_len].path = path.to_vec();
115        self.contexts[self.current_len].program = program;
116        self.contexts[self.current_len].caller = caller;
117        self.contexts[self.current_len].names = names;
118        self.contexts[self.current_len].accumulated_futures.clear();
119        self.contexts[self.current_len].is_async = is_async;
120        self.current_len += 1;
121    }
122
123    pub fn pop(&mut self) {
124        // We never actually pop the underlying Vec
125        // so we can reuse the storage of the hash
126        // tables.
127        assert!(self.len() > 0);
128        self.current_len -= 1;
129        self.contexts[self.current_len].names.clear();
130    }
131
132    /// Get the future accumulated by awaiting futures in the current function call.
133    ///
134    /// If the current code being interpreted is not in an async function, this
135    /// will of course be empty.
136    fn get_future(&mut self) -> Vec<AsyncExecution> {
137        assert!(self.len() > 0);
138        mem::take(&mut self.contexts[self.current_len - 1].accumulated_futures)
139    }
140
141    fn set(&mut self, path: &[Symbol], value: Value) {
142        assert!(self.current_len > 0);
143        self.last_mut().unwrap().names.insert(path.to_vec(), value);
144    }
145
146    pub fn add_future(&mut self, future: Vec<AsyncExecution>) {
147        assert!(self.current_len > 0);
148        self.contexts[self.current_len - 1].accumulated_futures.extend(future);
149    }
150
151    /// Are we currently in an async function?
152    fn is_async(&self) -> bool {
153        assert!(self.current_len > 0);
154        self.last().unwrap().is_async
155    }
156
157    pub fn current_program(&self) -> Option<Symbol> {
158        self.last().map(|c| c.program)
159    }
160
161    pub fn last(&self) -> Option<&FunctionContext> {
162        self.len().checked_sub(1).and_then(|i| self.contexts.get(i))
163    }
164
165    fn last_mut(&mut self) -> Option<&mut FunctionContext> {
166        self.len().checked_sub(1).and_then(|i| self.contexts.get_mut(i))
167    }
168}
169
170#[derive(Clone, Debug)]
171pub enum AleoContext {
172    Closure(Closure),
173    Function(SvmFunction),
174    Finalize(Finalize),
175}
176
177/// A Leo construct to be evauated.
178#[derive(Clone, Debug)]
179pub enum Element {
180    /// A Leo statement.
181    Statement(Statement),
182
183    /// A Leo expression. The optional type is an optional "expected type" for the expression. It helps when trying to
184    /// resolve an unsuffixed literal.
185    Expression(Expression, Option<Type>),
186
187    /// A Leo block.
188    ///
189    /// We have a separate variant for Leo blocks for two reasons:
190    /// 1. In a ConditionalExpression, the `then` block is stored
191    ///    as just a Block with no statement, and
192    /// 2. We need to remember if a Block came from a function body,
193    ///    so that if such a block ends, we know to push a `Unit` to
194    ///    the values stack.
195    Block {
196        block: Block,
197        function_body: bool,
198    },
199
200    AleoExecution {
201        context: Box<AleoContext>,
202        registers: IndexMap<u64, Value>,
203        instruction_index: usize,
204    },
205
206    DelayedCall(Location),
207    DelayedAsyncBlock {
208        program: Symbol,
209        block: NodeID,
210        names: HashMap<Vec<Symbol>, Value>,
211    },
212}
213
214impl Element {
215    pub fn span(&self) -> Span {
216        use Element::*;
217        match self {
218            Statement(statement) => statement.span(),
219            Expression(expression, _) => expression.span(),
220            Block { block, .. } => block.span(),
221            AleoExecution { .. } | DelayedCall(..) | DelayedAsyncBlock { .. } => Default::default(),
222        }
223    }
224}
225
226/// A frame of execution, keeping track of the Element next to
227/// be executed and the number of steps we've done so far.
228#[derive(Clone, Debug)]
229pub struct Frame {
230    pub step: usize,
231    pub element: Element,
232    pub user_initiated: bool,
233}
234
235#[derive(Clone, Debug)]
236pub enum FunctionVariant {
237    Leo(Function),
238    AleoClosure(Closure),
239    AleoFunction(SvmFunction),
240}
241
242/// Tracks the current execution state - a cursor into the running program.
243#[derive(Clone, Debug)]
244pub struct Cursor {
245    /// Stack of execution frames, with the one currently to be executed on top.
246    pub frames: Vec<Frame>,
247
248    /// Stack of values from evaluated expressions.
249    ///
250    /// Each time an expression completes evaluation, a value is pushed here.
251    pub values: Vec<Value>,
252
253    /// All functions (or transitions or inlines) in any program being interpreted.
254    pub functions: HashMap<Location, FunctionVariant>,
255
256    /// All the async blocks encountered. We identify them by their `NodeID`.
257    pub async_blocks: HashMap<NodeID, Block>,
258
259    /// Consts are stored here.
260    pub globals: HashMap<Location, Value>,
261
262    pub user_values: HashMap<Vec<Symbol>, Value>,
263
264    pub mappings: HashMap<Location, HashMap<Value, Value>>,
265
266    /// For each struct type, we only need to remember the names of its members, in order.
267    pub structs: HashMap<Vec<Symbol>, IndexMap<Symbol, Type>>,
268
269    /// For each record type, we index by program name and path, and remember its members
270    /// except `owner`.
271    pub records: HashMap<(Symbol, Vec<Symbol>), IndexMap<Symbol, Type>>,
272
273    pub futures: Vec<AsyncExecution>,
274
275    pub contexts: ContextStack,
276
277    // The signer's address.
278    pub signer: Value,
279
280    pub rng: ChaCha20Rng,
281
282    pub block_height: u32,
283
284    pub block_timestamp: i64,
285
286    pub really_async: bool,
287
288    pub program: Option<Symbol>,
289
290    pub network: NetworkName,
291
292    pub private_key: String,
293}
294
295impl CoreFunctionHelper for Cursor {
296    fn pop_value_impl(&mut self) -> Option<Value> {
297        self.values.pop()
298    }
299
300    fn set_block_height(&mut self, height: u32) {
301        self.block_height = height;
302    }
303
304    fn set_block_timestamp(&mut self, timestamp: i64) {
305        self.block_timestamp = timestamp;
306    }
307
308    fn set_signer(&mut self, private_key: String) -> Result<()> {
309        // Get the address from the private key.
310        let address = match PrivateKey::<TestnetV0>::from_str(&private_key.replace("\"", ""))
311            .and_then(|pk| Address::<TestnetV0>::try_from(&pk))
312        {
313            Ok(address) => address.into(),
314            Err(_) => halt_no_span!("Invalid private key provided for signer."),
315        };
316        // Set the private key
317        self.private_key = private_key;
318        // Set the signer.
319        self.signer = address;
320
321        Ok(())
322    }
323
324    fn lookup_mapping(&self, program: Option<Symbol>, name: Symbol) -> Option<&HashMap<Value, Value>> {
325        Cursor::lookup_mapping(self, program, name)
326    }
327
328    fn lookup_mapping_mut(&mut self, program: Option<Symbol>, name: Symbol) -> Option<&mut HashMap<Value, Value>> {
329        Cursor::lookup_mapping_mut(self, program, name)
330    }
331
332    fn rng(&mut self) -> Option<&mut ChaCha20Rng> {
333        Some(&mut self.rng)
334    }
335}
336
337impl Cursor {
338    /// `really_async` indicates we should really delay execution of async function calls until the user runs them.
339    pub fn new(
340        really_async: bool,
341        private_key: String,
342        block_height: u32,
343        block_timestamp: i64,
344        network: NetworkName,
345    ) -> Self {
346        let mut cursor = Cursor {
347            frames: Default::default(),
348            values: Default::default(),
349            functions: Default::default(),
350            async_blocks: Default::default(),
351            globals: Default::default(),
352            user_values: Default::default(),
353            mappings: Default::default(),
354            structs: Default::default(),
355            records: Default::default(),
356            contexts: Default::default(),
357            futures: Default::default(),
358            rng: ChaCha20Rng::from_entropy(),
359            signer: Default::default(),
360            block_height,
361            block_timestamp,
362            really_async,
363            program: None,
364            network,
365            private_key: Default::default(),
366        };
367
368        // Set the default private key.
369        cursor.set_signer(private_key).expect("The default private key should be valid.");
370
371        cursor
372    }
373
374    // Clears the state of the cursor, but keeps the program definitions.
375    pub fn clear(&mut self) {
376        self.frames.clear();
377        self.values.clear();
378        self.mappings.iter_mut().for_each(|(_, map)| map.clear());
379        self.contexts = Default::default();
380        self.futures.clear();
381    }
382
383    fn set_place(
384        new_value: Value,
385        this_value: &mut Value,
386        places: &mut dyn Iterator<Item = &Expression>,
387        indices: &mut dyn Iterator<Item = Value>,
388    ) -> Result<()> {
389        match places.next() {
390            None => *this_value = new_value,
391            Some(Expression::ArrayAccess(_access)) => {
392                let index = indices.next().unwrap();
393                let index = index.as_u32().unwrap() as usize;
394
395                let mut index_value = this_value.array_index(index).expect("Type");
396                Self::set_place(new_value, &mut index_value, places, indices)?;
397
398                if this_value.array_index_set(index, index_value).is_none() {
399                    halt_no_span!("Invalid array assignment");
400                }
401            }
402            Some(Expression::TupleAccess(access)) => {
403                let index = access.index.value();
404                let mut index_value = this_value.tuple_index(index).expect("Type");
405                Self::set_place(new_value, &mut index_value, places, indices)?;
406                if this_value.tuple_index_set(index, index_value).is_none() {
407                    halt_no_span!("Invalid tuple assignment");
408                }
409            }
410            Some(Expression::MemberAccess(access)) => {
411                let mut access_value = this_value.member_access(access.name.name).expect("Type");
412                Self::set_place(new_value, &mut access_value, places, indices)?;
413                if this_value.member_set(access.name.name, access_value).is_none() {
414                    halt_no_span!("Invalid member set");
415                }
416            }
417            Some(Expression::Path(_path)) => {
418                Self::set_place(new_value, this_value, places, indices)?;
419            }
420            Some(place) => halt_no_span!("Invalid place for assignment: {place}"),
421        }
422
423        Ok(())
424    }
425
426    pub fn assign(&mut self, value: Value, place: &Expression, indices: &mut dyn Iterator<Item = Value>) -> Result<()> {
427        let mut places = vec![place];
428        let indices: Vec<Value> = indices.collect();
429
430        let path: &Path;
431
432        loop {
433            match places.last().unwrap() {
434                Expression::ArrayAccess(access) => places.push(&access.array),
435                Expression::TupleAccess(access) => places.push(&access.tuple),
436                Expression::MemberAccess(access) => places.push(&access.inner),
437                Expression::Path(path_) => {
438                    path = path_;
439                    break;
440                }
441                place @ (Expression::AssociatedConstant(..)
442                | Expression::AssociatedFunction(..)
443                | Expression::Async(..)
444                | Expression::Array(..)
445                | Expression::Binary(..)
446                | Expression::Call(..)
447                | Expression::Cast(..)
448                | Expression::Err(..)
449                | Expression::Literal(..)
450                | Expression::Locator(..)
451                | Expression::Repeat(..)
452                | Expression::Struct(..)
453                | Expression::Ternary(..)
454                | Expression::Tuple(..)
455                | Expression::Unary(..)
456                | Expression::Unit(..)) => halt_no_span!("Invalid place for assignment: {place}"),
457            }
458        }
459
460        let full_name = self.to_absolute_path(&path.as_symbols());
461
462        let mut leo_value = self.lookup(&full_name).unwrap_or(Value::make_unit());
463
464        // Do an ad hoc evaluation of the lhs of the assignment to determine its type.
465        let mut temp_value = leo_value.clone();
466        let mut indices_iter = indices.iter();
467
468        for place in places.iter().rev() {
469            match place {
470                Expression::ArrayAccess(_access) => {
471                    let next_index = indices_iter.next().unwrap();
472                    temp_value = temp_value.array_index(next_index.as_u32().unwrap() as usize).unwrap();
473                }
474                Expression::TupleAccess(access) => {
475                    temp_value = temp_value.tuple_index(access.index.value()).unwrap();
476                }
477                Expression::MemberAccess(access) => {
478                    temp_value = temp_value.member_access(access.name.name).unwrap();
479                }
480                Expression::Path(_path) =>
481                    // temp_value is already set to leo_value
482                    {}
483                _ => panic!("Can't happen."),
484            }
485        }
486
487        let ty = temp_value.get_numeric_type();
488        let value = value.resolve_if_unsuffixed(&ty, place.span())?;
489
490        Self::set_place(value, &mut leo_value, &mut places.into_iter().rev(), &mut indices.into_iter())?;
491        self.set_variable(&full_name, leo_value);
492        Ok(())
493    }
494
495    pub fn set_program(&mut self, program: &str) {
496        let p = program.strip_suffix(".aleo").unwrap_or(program);
497        self.program = Some(Symbol::intern(p));
498    }
499
500    pub fn current_program(&self) -> Option<Symbol> {
501        self.contexts.current_program().or(self.program)
502    }
503
504    pub fn increment_step(&mut self) {
505        let Some(Frame { step, .. }) = self.frames.last_mut() else {
506            panic!("frame expected");
507        };
508        *step += 1;
509    }
510
511    fn new_caller(&self) -> Value {
512        if let Some(function_context) = self.contexts.last() {
513            let program_id = ProgramID::<TestnetV0>::from_str(&format!("{}.aleo", function_context.program))
514                .expect("should be able to create ProgramID");
515            program_id.to_address().expect("should be able to convert to address").into()
516        } else {
517            self.signer.clone()
518        }
519    }
520
521    fn pop_value(&mut self) -> Result<Value> {
522        match self.values.pop() {
523            Some(v) => Ok(v),
524            None => {
525                Err(InterpreterHalt::new("value expected - this may be a bug in the Leo interpreter".to_string())
526                    .into())
527            }
528        }
529    }
530
531    fn lookup(&self, name: &[Symbol]) -> Option<Value> {
532        if let Some(context) = self.contexts.last() {
533            let option_value =
534                context.names.get(name).or_else(|| self.globals.get(&Location::new(context.program, name.to_vec())));
535            if option_value.is_some() {
536                return option_value.cloned();
537            }
538        };
539
540        self.user_values.get(name).cloned()
541    }
542
543    pub fn lookup_mapping(&self, program: Option<Symbol>, name: Symbol) -> Option<&HashMap<Value, Value>> {
544        let Some(program) = program.or_else(|| self.current_program()) else {
545            panic!("no program for mapping lookup");
546        };
547        // mappings can only show up in the top level program scope
548        self.mappings.get(&Location::new(program, vec![name]))
549    }
550
551    pub fn lookup_mapping_mut(&mut self, program: Option<Symbol>, name: Symbol) -> Option<&mut HashMap<Value, Value>> {
552        let Some(program) = program.or_else(|| self.current_program()) else {
553            panic!("no program for mapping lookup");
554        };
555        // mappings can only show up in the top level program scope
556        self.mappings.get_mut(&Location::new(program, vec![name]))
557    }
558
559    fn lookup_function(&self, program: Symbol, name: &[Symbol]) -> Option<FunctionVariant> {
560        self.functions.get(&Location::new(program, name.to_vec())).cloned()
561    }
562
563    fn set_variable(&mut self, path: &[Symbol], value: Value) {
564        if self.contexts.len() > 0 {
565            self.contexts.set(path, value);
566        } else {
567            self.user_values.insert(path.to_vec(), value);
568        }
569    }
570
571    /// Execute the whole step of the current Element.
572    ///
573    /// That is, perform a step, and then finish all statements and expressions that have been pushed,
574    /// until we're ready for the next step of the current Element (if there is one).
575    pub fn whole_step(&mut self) -> Result<StepResult> {
576        let frames_len = self.frames.len();
577        let initial_result = self.step()?;
578        if !initial_result.finished {
579            while self.frames.len() > frames_len {
580                self.step()?;
581            }
582        }
583        Ok(initial_result)
584    }
585
586    /// Step `over` the current Element.
587    ///
588    /// That is, continue executing until the current Element is finished.
589    pub fn over(&mut self) -> Result<StepResult> {
590        let frames_len = self.frames.len();
591        loop {
592            match self.frames.len().cmp(&frames_len) {
593                Ordering::Greater => {
594                    self.step()?;
595                }
596                Ordering::Equal => {
597                    let result = self.step()?;
598                    if result.finished {
599                        return Ok(result);
600                    }
601                }
602                Ordering::Less => {
603                    // This can happen if, for instance, a `return` was encountered,
604                    // which means we exited the function we were evaluating and the
605                    // frame stack was truncated.
606                    return Ok(StepResult { finished: true, value: None });
607                }
608            }
609        }
610    }
611
612    pub fn step_block(&mut self, block: &Block, function_body: bool, step: usize) -> bool {
613        let len = self.frames.len();
614
615        let done = match step {
616            0 => {
617                for statement in block.statements.iter().rev() {
618                    self.frames.push(Frame {
619                        element: Element::Statement(statement.clone()),
620                        step: 0,
621                        user_initiated: false,
622                    });
623                }
624                false
625            }
626            1 if function_body => {
627                self.values.push(Value::make_unit());
628                self.contexts.pop();
629                true
630            }
631            1 => true,
632            _ => unreachable!(),
633        };
634
635        if done {
636            assert_eq!(len, self.frames.len());
637            self.frames.pop();
638        } else {
639            self.frames[len - 1].step += 1;
640        }
641
642        done
643    }
644
645    /// Returns the full absolute path by prefixing `name` with the current module path.
646    /// If no context is available, returns `name` as-is.
647    fn to_absolute_path(&self, name: &[Symbol]) -> Vec<Symbol> {
648        if let Some(context) = self.contexts.last() {
649            let mut full_name = context.path.clone();
650            full_name.pop(); // This pops the function name, keeping only the module prefix 
651            full_name.extend(name);
652            full_name
653        } else {
654            name.to_vec()
655        }
656    }
657
658    fn step_statement(&mut self, statement: &Statement, step: usize) -> Result<bool> {
659        let len = self.frames.len();
660        // Push a new expression frame with an optional expected type for the expression
661        let mut push = |expression: &Expression, ty: &Option<Type>| {
662            self.frames.push(Frame {
663                element: Element::Expression(expression.clone(), ty.clone()),
664                step: 0,
665                user_initiated: false,
666            })
667        };
668
669        let done = match statement {
670            Statement::Assert(assert) if step == 0 => {
671                match &assert.variant {
672                    AssertVariant::Assert(x) => push(x, &Some(Type::Boolean)),
673                    AssertVariant::AssertEq(x, y) | AssertVariant::AssertNeq(x, y) => {
674                        push(y, &None);
675                        push(x, &None);
676                    }
677                };
678                false
679            }
680            Statement::Assert(assert) if step == 1 => {
681                match &assert.variant {
682                    AssertVariant::Assert(..) => {
683                        let value = self.pop_value()?;
684                        match value.try_into() {
685                            Ok(true) => {}
686                            Ok(false) => halt!(assert.span(), "assert failure"),
687                            _ => tc_fail!(),
688                        }
689                    }
690                    AssertVariant::AssertEq(..) => {
691                        let x = self.pop_value()?;
692                        let y = self.pop_value()?;
693                        if !x.eq(&y)? {
694                            halt!(assert.span(), "assert failure: {} != {}", y, x);
695                        }
696                    }
697
698                    AssertVariant::AssertNeq(..) => {
699                        let x = self.pop_value()?;
700                        let y = self.pop_value()?;
701                        if x.eq(&y)? {
702                            halt!(assert.span(), "assert failure: {} == {}", y, x);
703                        }
704                    }
705                };
706                true
707            }
708            Statement::Assign(assign) if step == 0 => {
709                // Step 0: push the expression frame and any array index expression frames.
710                push(&assign.value, &None);
711                let mut place = &assign.place;
712                loop {
713                    match place {
714                        leo_ast::Expression::ArrayAccess(access) => {
715                            push(&access.index, &None);
716                            place = &access.array;
717                        }
718                        leo_ast::Expression::Path(..) => break,
719                        leo_ast::Expression::MemberAccess(access) => {
720                            place = &access.inner;
721                        }
722                        leo_ast::Expression::TupleAccess(access) => {
723                            place = &access.tuple;
724                        }
725                        _ => panic!("Can't happen"),
726                    }
727                }
728                false
729            }
730            Statement::Assign(assign) if step == 1 => {
731                // Step 1: set the variable (or place).
732                let mut index_count = 0;
733                let mut place = &assign.place;
734                loop {
735                    match place {
736                        leo_ast::Expression::ArrayAccess(access) => {
737                            index_count += 1;
738                            place = &access.array;
739                        }
740                        leo_ast::Expression::Path(..) => break,
741                        leo_ast::Expression::MemberAccess(access) => {
742                            place = &access.inner;
743                        }
744                        leo_ast::Expression::TupleAccess(access) => {
745                            place = &access.tuple;
746                        }
747                        _ => panic!("Can't happen"),
748                    }
749                }
750
751                // Get the value.
752                let value = self.pop_value()?;
753                let len = self.values.len();
754
755                // Get the indices.
756                let indices: Vec<Value> = self.values.drain(len - index_count..len).collect();
757
758                self.assign(value, &assign.place, &mut indices.into_iter())?;
759
760                true
761            }
762            Statement::Block(block) => return Ok(self.step_block(block, false, step)),
763            Statement::Conditional(conditional) if step == 0 => {
764                push(&conditional.condition, &Some(Type::Boolean));
765                false
766            }
767            Statement::Conditional(conditional) if step == 1 => {
768                match self.pop_value()?.try_into() {
769                    Ok(true) => self.frames.push(Frame {
770                        step: 0,
771                        element: Element::Block { block: conditional.then.clone(), function_body: false },
772                        user_initiated: false,
773                    }),
774                    Ok(false) => {
775                        if let Some(otherwise) = conditional.otherwise.as_ref() {
776                            self.frames.push(Frame {
777                                step: 0,
778                                element: Element::Statement(Statement::clone(otherwise)),
779                                user_initiated: false,
780                            })
781                        }
782                    }
783                    _ => tc_fail!(),
784                };
785                false
786            }
787            Statement::Conditional(_) if step == 2 => true,
788            Statement::Const(const_) if step == 0 => {
789                push(&const_.value, &Some(const_.type_.clone()));
790                false
791            }
792            Statement::Const(const_) if step == 1 => {
793                let value = self.pop_value()?;
794                self.set_variable(&self.to_absolute_path(&[const_.place.name]), value);
795                true
796            }
797            Statement::Definition(definition) if step == 0 => {
798                push(&definition.value, &definition.type_);
799                false
800            }
801            Statement::Definition(definition) if step == 1 => {
802                let value = self.pop_value()?;
803                match &definition.place {
804                    DefinitionPlace::Single(id) => self.set_variable(&self.to_absolute_path(&[id.name]), value),
805                    DefinitionPlace::Multiple(ids) => {
806                        for (i, id) in ids.iter().enumerate() {
807                            self.set_variable(
808                                &self.to_absolute_path(&[id.name]),
809                                value.tuple_index(i).expect("Place for definition should be a tuple."),
810                            );
811                        }
812                    }
813                }
814                true
815            }
816            Statement::Expression(expression) if step == 0 => {
817                push(&expression.expression, &None);
818                false
819            }
820            Statement::Expression(_) if step == 1 => {
821                self.values.pop();
822                true
823            }
824            Statement::Iteration(iteration) if step == 0 => {
825                assert!(!iteration.inclusive);
826                push(&iteration.stop, &iteration.type_.clone());
827                push(&iteration.start, &iteration.type_.clone());
828                false
829            }
830            Statement::Iteration(iteration) => {
831                // Currently there actually isn't a syntax in Leo for inclusive ranges.
832                let stop = self.pop_value()?;
833                let start = self.pop_value()?;
834                if start.eq(&stop)? {
835                    true
836                } else {
837                    let new_start = start.inc_wrapping().expect_tc(iteration.span())?;
838                    self.set_variable(&self.to_absolute_path(&[iteration.variable.name]), start);
839                    self.frames.push(Frame {
840                        step: 0,
841                        element: Element::Block { block: iteration.block.clone(), function_body: false },
842                        user_initiated: false,
843                    });
844                    self.values.push(new_start);
845                    self.values.push(stop);
846                    false
847                }
848            }
849            Statement::Return(return_) if step == 0 => {
850                // We really only need to care about the type of the output for Leo functions. Aleo functions and
851                // closures don't have to worry about unsuffixed literals
852                let output_type = self.contexts.last().and_then(|ctx| {
853                    self.lookup_function(ctx.program, &ctx.path).and_then(|variant| match variant {
854                        FunctionVariant::Leo(function) => Some(function.output_type.clone()),
855                        _ => None,
856                    })
857                });
858
859                self.frames.push(Frame {
860                    element: Element::Expression(return_.expression.clone(), output_type),
861                    step: 0,
862                    user_initiated: false,
863                });
864
865                false
866            }
867            Statement::Return(_) if step == 1 => loop {
868                let last_frame = self.frames.last().expect("a frame should be present");
869                match last_frame.element {
870                    Element::Expression(Expression::Call(_), _) | Element::DelayedCall(_) => {
871                        if self.contexts.is_async() {
872                            // Get rid of the Unit we previously pushed, and replace it with a Future.
873                            self.values.pop();
874                            self.values.push(self.contexts.get_future().into());
875                        }
876                        self.contexts.pop();
877                        return Ok(true);
878                    }
879                    _ => {
880                        self.frames.pop();
881                    }
882                }
883            },
884            _ => unreachable!(),
885        };
886
887        if done {
888            assert_eq!(len, self.frames.len());
889            self.frames.pop();
890        } else {
891            self.frames[len - 1].step += 1;
892        }
893
894        Ok(done)
895    }
896
897    fn step_expression(&mut self, expression: &Expression, expected_ty: &Option<Type>, step: usize) -> Result<bool> {
898        let len = self.frames.len();
899
900        macro_rules! push {
901            () => {
902                |expression: &Expression, expected_ty: &Option<Type>| {
903                    self.frames.push(Frame {
904                        element: Element::Expression(expression.clone(), expected_ty.clone()),
905                        step: 0,
906                        user_initiated: false,
907                    })
908                }
909            };
910        }
911
912        if let Some(value) = match expression {
913            Expression::ArrayAccess(array) if step == 0 => {
914                push!()(&array.index, &None);
915                push!()(&array.array, &None);
916                None
917            }
918            Expression::ArrayAccess(array_expr) if step == 1 => {
919                let span = array_expr.span();
920                let index = self.pop_value()?;
921                let array = self.pop_value()?;
922
923                // Local helper function to convert a Value into usize
924                fn to_usize(value: &Value, span: Span) -> Result<usize> {
925                    let value = value.resolve_if_unsuffixed(&Some(Type::Integer(leo_ast::IntegerType::U32)), span)?;
926                    Ok(value.as_u32().expect_tc(span)? as usize)
927                }
928
929                let index_usize = to_usize(&index, span)?;
930
931                Some(array.array_index(index_usize).expect_tc(span)?)
932            }
933
934            Expression::Async(AsyncExpression { block, .. }) if step == 0 => {
935                // Keep track of the async block, but nothing else to do at this point
936                self.async_blocks.insert(block.id, block.clone());
937                None
938            }
939            Expression::Async(AsyncExpression { block, .. }) if step == 1 => {
940                // Keep track of this block as a `Future` containing an `AsyncExecution` but nothing else to do here.
941                // The block actually executes when an `await` is called on its future.
942                if let Some(context) = self.contexts.last() {
943                    let async_ex = AsyncExecution::AsyncBlock {
944                        containing_function: Location::new(context.program, context.path.clone()),
945                        block: block.id,
946                        names: context.names.clone().into_iter().collect(),
947                    };
948                    self.values.push(vec![async_ex].into());
949                }
950                None
951            }
952            Expression::Async(_) if step == 2 => Some(self.pop_value()?),
953
954            Expression::MemberAccess(access) => match &access.inner {
955                Expression::Path(path) if *path.as_symbols() == vec![sym::SelfLower] => match access.name.name {
956                    sym::signer => Some(self.signer.clone()),
957                    sym::caller => {
958                        if let Some(function_context) = self.contexts.last() {
959                            Some(function_context.caller.clone())
960                        } else {
961                            Some(self.signer.clone())
962                        }
963                    }
964                    sym::address => {
965                        // A helper function to convert a program ID string to an address value.
966                        fn program_to_address<N: Network>(program_id: &str) -> Result<Value> {
967                            let Ok(program_id) = ProgramID::<N>::from_str(&format!("{program_id}.aleo")) else {
968                                halt_no_span!("Failed to parse program ID");
969                            };
970                            let Ok(address) = program_id.to_address() else {
971                                halt_no_span!("Failed to convert program ID to address");
972                            };
973                            let Ok(value) = Value::from_str(&address.to_string()) else {
974                                halt_no_span!("Failed to convert address to value");
975                            };
976                            Ok(value)
977                        }
978                        // Get the current program.
979                        let Some(program) = self.current_program() else {
980                            halt_no_span!("No program context for address");
981                        };
982                        let result = match self.network {
983                            NetworkName::TestnetV0 => program_to_address::<TestnetV0>(&program.to_string())?,
984                            NetworkName::MainnetV0 => program_to_address::<MainnetV0>(&program.to_string())?,
985                            NetworkName::CanaryV0 => program_to_address::<CanaryV0>(&program.to_string())?,
986                        };
987                        Some(result)
988                    }
989                    _ => halt!(access.span(), "unknown member of self"),
990                },
991                Expression::Path(path) if *path.as_symbols() == vec![sym::block] => match access.name.name {
992                    sym::height => Some(self.block_height.into()),
993                    sym::timestamp => Some(self.block_timestamp.into()),
994                    _ => halt!(access.span(), "unknown member of block"),
995                },
996
997                // Otherwise, we just have a normal struct member access.
998                _ if step == 0 => {
999                    push!()(&access.inner, &None);
1000                    None
1001                }
1002                _ if step == 1 => {
1003                    let struct_ = self.values.pop().expect_tc(access.span())?;
1004                    let value = struct_.member_access(access.name.name).expect_tc(access.span())?;
1005                    Some(value)
1006                }
1007                _ => unreachable!("we've actually covered all possible patterns above"),
1008            },
1009            Expression::TupleAccess(tuple_access) if step == 0 => {
1010                push!()(&tuple_access.tuple, &None);
1011                None
1012            }
1013            Expression::TupleAccess(tuple_access) if step == 1 => {
1014                let Some(value) = self.values.pop() else { tc_fail!() };
1015                if let Some(result) = value.tuple_index(tuple_access.index.value()) {
1016                    Some(result)
1017                } else {
1018                    halt!(tuple_access.span(), "Tuple index out of range");
1019                }
1020            }
1021            Expression::Array(array) if step == 0 => {
1022                let element_type = expected_ty.clone().and_then(|ty| match ty {
1023                    Type::Array(ArrayType { element_type, .. }) => Some(*element_type),
1024                    _ => None,
1025                });
1026
1027                array.elements.iter().rev().for_each(|array| push!()(array, &element_type));
1028                None
1029            }
1030            Expression::Array(array) if step == 1 => {
1031                let len = self.values.len();
1032                let array_values = self.values.drain(len - array.elements.len()..);
1033                Some(Value::make_array(array_values))
1034            }
1035            Expression::Repeat(repeat) if step == 0 => {
1036                let element_type = expected_ty.clone().and_then(|ty| match ty {
1037                    Type::Array(ArrayType { element_type, .. }) => Some(*element_type),
1038                    _ => None,
1039                });
1040
1041                push!()(&repeat.count, &None);
1042                push!()(&repeat.expr, &element_type);
1043                None
1044            }
1045            Expression::Repeat(repeat) if step == 1 => {
1046                let count = self.pop_value()?;
1047                let expr = self.pop_value()?;
1048                let count_resolved = count
1049                    .resolve_if_unsuffixed(&Some(Type::Integer(leo_ast::IntegerType::U32)), repeat.count.span())?;
1050                Some(Value::make_array(std::iter::repeat_n(
1051                    expr,
1052                    count_resolved.as_u32().expect_tc(repeat.span())? as usize,
1053                )))
1054            }
1055            Expression::AssociatedConstant(constant) if step == 0 => {
1056                let Type::Identifier(type_ident) = constant.ty else {
1057                    tc_fail!();
1058                };
1059                let Some(core_constant) = CoreConstant::from_symbols(type_ident.name, constant.name.name) else {
1060                    halt!(constant.span(), "Unknown constant {constant}");
1061                };
1062                match core_constant {
1063                    CoreConstant::GroupGenerator => Some(Value::generator()),
1064                }
1065            }
1066            Expression::AssociatedFunction(function) if step == 0 => {
1067                let Some(core_function) = CoreFunction::try_from(function).ok() else {
1068                    halt!(function.span(), "Unkown core function {function}");
1069                };
1070
1071                // We want to push expressions for each of the arguments... except for mappings,
1072                // because we don't look them up as Values.
1073                match core_function {
1074                    CoreFunction::Get | CoreFunction::MappingRemove | CoreFunction::MappingContains => {
1075                        push!()(&function.arguments[1], &None);
1076                    }
1077                    CoreFunction::MappingGetOrUse | CoreFunction::Set => {
1078                        push!()(&function.arguments[2], &None);
1079                        push!()(&function.arguments[1], &None);
1080                    }
1081                    CoreFunction::CheatCodePrintMapping => {
1082                        // Do nothing, as we don't need to evaluate the mapping.
1083                    }
1084                    _ => function.arguments.iter().rev().for_each(|arg| push!()(arg, &None)),
1085                }
1086                None
1087            }
1088            Expression::AssociatedFunction(function) if step == 1 => {
1089                let Some(core_function) = CoreFunction::try_from(function).ok() else {
1090                    halt!(function.span(), "Unkown core function {function}");
1091                };
1092
1093                let span = function.span();
1094
1095                if let CoreFunction::FutureAwait = core_function {
1096                    let value = self.pop_value()?;
1097                    let Some(asyncs) = value.as_future() else {
1098                        halt!(span, "Invalid value for await: {value}");
1099                    };
1100                    for async_execution in asyncs {
1101                        match async_execution {
1102                            AsyncExecution::AsyncFunctionCall { function, arguments } => {
1103                                self.values.extend(arguments.iter().cloned());
1104                                self.frames.push(Frame {
1105                                    step: 0,
1106                                    element: Element::DelayedCall(function.clone()),
1107                                    user_initiated: false,
1108                                });
1109                            }
1110                            AsyncExecution::AsyncBlock { containing_function, block, names, .. } => {
1111                                self.frames.push(Frame {
1112                                    step: 0,
1113                                    element: Element::DelayedAsyncBlock {
1114                                        program: containing_function.program,
1115                                        block: *block,
1116                                        // Keep track of all the known variables up to this point.
1117                                        // These are available to use inside the block when we actually execute it.
1118                                        names: names.clone().into_iter().collect(),
1119                                    },
1120                                    user_initiated: false,
1121                                });
1122                            }
1123                        }
1124                    }
1125                    // For an await, we have one extra step - first we must evaluate the delayed call.
1126                    None
1127                } else {
1128                    let value = evaluate_core_function(self, core_function.clone(), &function.arguments, span)?;
1129                    assert!(value.is_some());
1130                    value
1131                }
1132            }
1133            Expression::AssociatedFunction(function) if step == 2 => {
1134                let Some(core_function) = CoreFunction::try_from(function).ok() else {
1135                    halt!(function.span(), "Unkown core function {function}");
1136                };
1137                assert!(core_function == CoreFunction::FutureAwait);
1138                Some(Value::make_unit())
1139            }
1140            Expression::Binary(binary) if step == 0 => {
1141                use BinaryOperation::*;
1142
1143                // Determine the expected types for the right and left operands based on the operation
1144                let (right_ty, left_ty) = match binary.op {
1145                    // Multiplications that return a `Group` can take `Scalar * Group` or `Group * Scalar`.
1146                    // No way to know at this stage.
1147                    Mul if matches!(expected_ty, Some(Type::Group)) => (None, None),
1148
1149                    // Boolean operations don't require expected type propagation
1150                    And | Or | Nand | Nor | Eq | Neq | Lt | Gt | Lte | Gte => (None, None),
1151
1152                    // Exponentiation (Pow) may require specific typing if expected to be a Field
1153                    Pow => {
1154                        let right_ty = if matches!(expected_ty, Some(Type::Field)) {
1155                            Some(Type::Field) // Enforce Field type on exponent if expected
1156                        } else {
1157                            None // Otherwise, don't constrain the exponent
1158                        };
1159                        (right_ty, expected_ty.clone()) // Pass the expected type to the base
1160                    }
1161
1162                    // Bitwise shifts and wrapped exponentiation:
1163                    // Typically only the left operand should conform to the expected type
1164                    Shl | ShlWrapped | Shr | ShrWrapped | PowWrapped => (None, expected_ty.clone()),
1165
1166                    // Default case: propagate expected type to both operands
1167                    _ => (expected_ty.clone(), expected_ty.clone()),
1168                };
1169
1170                // Push operands onto the stack for evaluation in right-to-left order
1171                push!()(&binary.right, &right_ty);
1172                push!()(&binary.left, &left_ty);
1173
1174                None
1175            }
1176            Expression::Binary(binary) if step == 1 => {
1177                let rhs = self.pop_value()?;
1178                let lhs = self.pop_value()?;
1179                Some(evaluate_binary(binary.span, binary.op, &lhs, &rhs, expected_ty)?)
1180            }
1181
1182            Expression::Call(call) if step == 0 => {
1183                // Resolve the function's program and name
1184                let (function_program, function_path) = {
1185                    let maybe_program = call.program.or_else(|| self.current_program());
1186                    if let Some(program) = maybe_program {
1187                        (program, self.to_absolute_path(&call.function.as_symbols()))
1188                    } else {
1189                        halt!(call.span, "No current program");
1190                    }
1191                };
1192
1193                // Look up the function variant (Leo, AleoClosure, or AleoFunction)
1194                let Some(function_variant) = self.lookup_function(function_program, &function_path) else {
1195                    halt!(call.span, "unknown function {function_program}.aleo/{}", function_path.iter().format("::"));
1196                };
1197
1198                // Extract const parameter and input types based on the function variant
1199                let (const_param_types, input_types) = match function_variant {
1200                    FunctionVariant::Leo(function) => (
1201                        function.const_parameters.iter().map(|p| p.type_.clone()).collect::<Vec<_>>(),
1202                        function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>(),
1203                    ),
1204                    FunctionVariant::AleoClosure(closure) => {
1205                        let function = leo_ast::FunctionStub::from_closure(&closure, function_program);
1206                        (vec![], function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>())
1207                    }
1208                    FunctionVariant::AleoFunction(svm_function) => {
1209                        let function = leo_ast::FunctionStub::from_function_core(&svm_function, function_program);
1210                        (vec![], function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>())
1211                    }
1212                };
1213
1214                // Push arguments (in reverse order) with corresponding input types
1215                call.arguments
1216                    .iter()
1217                    .rev()
1218                    .zip(input_types.iter().rev())
1219                    .for_each(|(arg, ty)| push!()(arg, &Some(ty.clone())));
1220
1221                // Push const arguments (in reverse order) with corresponding const param types
1222                call.const_arguments
1223                    .iter()
1224                    .rev()
1225                    .zip(const_param_types.iter().rev())
1226                    .for_each(|(arg, ty)| push!()(arg, &Some(ty.clone())));
1227
1228                None
1229            }
1230
1231            Expression::Call(call) if step == 1 => {
1232                let len = self.values.len();
1233                let (program, path) = {
1234                    let maybe_program = call.program.or_else(|| self.current_program());
1235                    if let Some(program) = maybe_program {
1236                        (program, call.function.as_symbols())
1237                    } else {
1238                        halt!(call.span, "No current program");
1239                    }
1240                };
1241                // It's a bit cheesy to collect the arguments into a Vec first, but it's the easiest way
1242                // to handle lifetimes here.
1243                let arguments: Vec<Value> =
1244                    self.values.drain(len - call.arguments.len() - call.const_arguments.len()..).collect();
1245                self.do_call(
1246                    program,
1247                    &self.to_absolute_path(&path),
1248                    arguments.into_iter(),
1249                    false, // finalize
1250                    call.span(),
1251                )?;
1252                None
1253            }
1254            Expression::Call(_call) if step == 2 => Some(self.pop_value()?),
1255            Expression::Cast(cast) if step == 0 => {
1256                push!()(&cast.expression, &None);
1257                None
1258            }
1259            Expression::Cast(cast) if step == 1 => {
1260                let span = cast.span();
1261                let arg = self.pop_value()?;
1262                match arg.cast(&cast.type_) {
1263                    Some(value) => Some(value),
1264                    None => return Err(InterpreterHalt::new_spanned("cast failure".to_string(), span).into()),
1265                }
1266            }
1267            Expression::Err(_) => todo!(),
1268            Expression::Path(path) if step == 0 => {
1269                Some(self.lookup(&self.to_absolute_path(&path.as_symbols())).expect_tc(path.span())?)
1270            }
1271            Expression::Literal(literal) if step == 0 => Some(literal_to_value(literal, expected_ty)?),
1272            Expression::Locator(_locator) => todo!(),
1273            Expression::Struct(struct_) if step == 0 => {
1274                let members =
1275                    self.structs.get(&self.to_absolute_path(&struct_.path.as_symbols())).expect_tc(struct_.span())?;
1276                for StructVariableInitializer { identifier: field_init_name, expression: init, .. } in &struct_.members
1277                {
1278                    let Some(type_) = members.get(&field_init_name.name) else { tc_fail!() };
1279                    push!()(
1280                        init.as_ref().unwrap_or(&Expression::Path(Path::from(*field_init_name))),
1281                        &Some(type_.clone()),
1282                    )
1283                }
1284
1285                None
1286            }
1287            Expression::Struct(struct_) if step == 1 => {
1288                // Collect all the key/value pairs into a HashMap.
1289                let mut contents_tmp = HashMap::with_capacity(struct_.members.len());
1290                for initializer in struct_.members.iter() {
1291                    let name = initializer.identifier.name;
1292                    let value = self.pop_value()?;
1293                    contents_tmp.insert(name, value);
1294                }
1295
1296                // And now put them into an IndexMap in the correct order.
1297                let members =
1298                    self.structs.get(&self.to_absolute_path(&struct_.path.as_symbols())).expect_tc(struct_.span())?;
1299                let contents = members.iter().map(|(identifier, _)| {
1300                    (*identifier, contents_tmp.remove(identifier).expect("we just inserted this"))
1301                });
1302
1303                // TODO: this only works for structs defined in the top level module.. must figure
1304                // something out for structs defined in modules
1305                Some(Value::make_struct(contents, self.current_program().unwrap(), struct_.path.as_symbols()))
1306            }
1307            Expression::Ternary(ternary) if step == 0 => {
1308                push!()(&ternary.condition, &None);
1309                None
1310            }
1311            Expression::Ternary(ternary) if step == 1 => {
1312                let condition = self.pop_value()?;
1313                match condition.try_into() {
1314                    Ok(true) => push!()(&ternary.if_true, &None),
1315                    Ok(false) => push!()(&ternary.if_false, &None),
1316                    _ => halt!(ternary.span(), "Invalid type for ternary expression {ternary}"),
1317                }
1318                None
1319            }
1320            Expression::Ternary(_) if step == 2 => Some(self.pop_value()?),
1321            Expression::Tuple(tuple) if step == 0 => {
1322                tuple.elements.iter().rev().for_each(|t| push!()(t, &None));
1323                None
1324            }
1325            Expression::Tuple(tuple) if step == 1 => {
1326                let len = self.values.len();
1327                let tuple_values = self.values.drain(len - tuple.elements.len()..);
1328                Some(Value::make_tuple(tuple_values))
1329            }
1330            Expression::Unary(unary) if step == 0 => {
1331                use UnaryOperation::*;
1332
1333                // Determine the expected type based on the unary operation
1334                let ty = match unary.op {
1335                    Inverse | Square | SquareRoot => Some(Type::Field), // These ops require Field operands
1336                    ToXCoordinate | ToYCoordinate => Some(Type::Group), // These ops apply to Group elements
1337                    _ => expected_ty.clone(),                           // Fallback to the externally expected type
1338                };
1339
1340                // Push the receiver expression with the computed type
1341                push!()(&unary.receiver, &ty);
1342
1343                None
1344            }
1345            Expression::Unary(unary) if step == 1 => {
1346                let value = self.pop_value()?;
1347                Some(evaluate_unary(unary.span, unary.op, &value, expected_ty)?)
1348            }
1349            Expression::Unit(_) if step == 0 => Some(Value::make_unit()),
1350            x => unreachable!("Unexpected expression {x}"),
1351        } {
1352            assert_eq!(self.frames.len(), len);
1353            self.frames.pop();
1354            self.values.push(value);
1355            Ok(true)
1356        } else {
1357            self.frames[len - 1].step += 1;
1358            Ok(false)
1359        }
1360    }
1361
1362    /// Execute one step of the current element.
1363    ///
1364    /// Many Leo constructs require multiple steps. For instance, when executing a conditional,
1365    /// the first step will push the condition expression to the stack. Once that has executed
1366    /// and we've returned to the conditional, we push the `then` or `otherwise` block to the
1367    /// stack. Once that has executed and we've returned to the conditional, the final step
1368    /// does nothing.
1369    pub fn step(&mut self) -> Result<StepResult> {
1370        if self.frames.is_empty() {
1371            return Err(InterpreterHalt::new("no execution frames available".into()).into());
1372        }
1373
1374        let Frame { element, step, user_initiated } = self.frames.last().expect("there should be a frame").clone();
1375        match element {
1376            Element::Block { block, function_body } => {
1377                let finished = self.step_block(&block, function_body, step);
1378                Ok(StepResult { finished, value: None })
1379            }
1380            Element::Statement(statement) => {
1381                let finished = self.step_statement(&statement, step)?;
1382                Ok(StepResult { finished, value: None })
1383            }
1384            Element::Expression(expression, expected_ty) => {
1385                let finished = self.step_expression(&expression, &expected_ty, step)?;
1386                let value = match (finished, user_initiated) {
1387                    (false, _) => None,
1388                    (true, false) => self.values.last().cloned(),
1389                    (true, true) => self.values.pop(),
1390                };
1391
1392                let maybe_future = if let Some(len) = value.as_ref().and_then(|val| val.tuple_len()) {
1393                    value.as_ref().unwrap().tuple_index(len - 1)
1394                } else {
1395                    value.clone()
1396                };
1397
1398                if let Some(asyncs) = maybe_future.as_ref().and_then(|fut| fut.as_future())
1399                    && user_initiated
1400                {
1401                    self.futures.extend(asyncs.iter().cloned());
1402                }
1403
1404                Ok(StepResult { finished, value })
1405            }
1406            Element::AleoExecution { .. } => {
1407                self.step_aleo()?;
1408                Ok(StepResult { finished: true, value: None })
1409            }
1410            Element::DelayedCall(gid) if step == 0 => {
1411                match self.lookup_function(gid.program, &gid.path).expect("function should exist") {
1412                    FunctionVariant::Leo(function) => {
1413                        assert!(function.variant == Variant::AsyncFunction);
1414                        let len = self.values.len();
1415                        let values: Vec<Value> = self.values.drain(len - function.input.len()..).collect();
1416                        self.contexts.push(
1417                            &gid.path,
1418                            gid.program,
1419                            self.signer.clone(),
1420                            true, // is_async
1421                            HashMap::new(),
1422                        );
1423                        let param_names = function.input.iter().map(|input| input.identifier.name);
1424                        for (name, value) in param_names.zip(values) {
1425                            self.set_variable(&self.to_absolute_path(&[name]), value);
1426                        }
1427                        self.frames.last_mut().unwrap().step = 1;
1428                        self.frames.push(Frame {
1429                            step: 0,
1430                            element: Element::Block { block: function.block.clone(), function_body: true },
1431                            user_initiated: false,
1432                        });
1433                        Ok(StepResult { finished: false, value: None })
1434                    }
1435                    FunctionVariant::AleoFunction(function) => {
1436                        let Some(finalize_f) = function.finalize_logic() else {
1437                            panic!("must have finalize logic for a delayed call");
1438                        };
1439                        let len = self.values.len();
1440                        let values_iter = self.values.drain(len - finalize_f.inputs().len()..);
1441                        self.contexts.push(
1442                            &gid.path,
1443                            gid.program,
1444                            self.signer.clone(),
1445                            true, // is_async
1446                            HashMap::new(),
1447                        );
1448                        self.frames.last_mut().unwrap().step = 1;
1449                        self.frames.push(Frame {
1450                            step: 0,
1451                            element: Element::AleoExecution {
1452                                context: AleoContext::Finalize(finalize_f.clone()).into(),
1453                                registers: values_iter.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1454                                instruction_index: 0,
1455                            },
1456                            user_initiated: false,
1457                        });
1458                        Ok(StepResult { finished: false, value: None })
1459                    }
1460                    FunctionVariant::AleoClosure(..) => panic!("A call to a closure can't be delayed"),
1461                }
1462            }
1463            Element::DelayedCall(_gid) => {
1464                assert_eq!(step, 1);
1465                let value = self.values.pop();
1466                self.frames.pop();
1467                Ok(StepResult { finished: true, value })
1468            }
1469            Element::DelayedAsyncBlock { program, block, names } if step == 0 => {
1470                self.contexts.push(
1471                    &[Symbol::intern("")],
1472                    program,
1473                    self.signer.clone(),
1474                    true,
1475                    names.clone().into_iter().collect(), // Set the known names to the previously preserved `names`.
1476                );
1477                self.frames.last_mut().unwrap().step = 1;
1478                self.frames.push(Frame {
1479                    step: 0,
1480                    element: Element::Block {
1481                        block: self.async_blocks.get(&block).unwrap().clone(),
1482                        function_body: true,
1483                    },
1484                    user_initiated: false,
1485                });
1486                Ok(StepResult { finished: false, value: None })
1487            }
1488            Element::DelayedAsyncBlock { .. } => {
1489                assert_eq!(step, 1);
1490                let value = self.values.pop();
1491                self.frames.pop();
1492                Ok(StepResult { finished: true, value })
1493            }
1494        }
1495    }
1496
1497    pub fn do_call(
1498        &mut self,
1499        function_program: Symbol,
1500        function_path: &[Symbol],
1501        arguments: impl Iterator<Item = Value>,
1502        finalize: bool,
1503        span: Span,
1504    ) -> Result<()> {
1505        let Some(function_variant) = self.lookup_function(function_program, function_path) else {
1506            halt!(span, "unknown function {function_program}.aleo/{}", function_path.iter().format("::"));
1507        };
1508        match function_variant {
1509            FunctionVariant::Leo(function) => {
1510                let caller = if matches!(function.variant, Variant::Transition | Variant::AsyncTransition) {
1511                    self.new_caller()
1512                } else {
1513                    self.signer.clone()
1514                };
1515                if self.really_async && function.variant == Variant::AsyncFunction {
1516                    // Don't actually run the call now.
1517                    let async_ex = AsyncExecution::AsyncFunctionCall {
1518                        function: Location::new(function_program, function_path.to_vec()),
1519                        arguments: arguments.collect(),
1520                    };
1521                    self.values.push(vec![async_ex].into());
1522                } else {
1523                    let is_async = function.variant == Variant::AsyncFunction;
1524                    self.contexts.push(function_path, function_program, caller, is_async, HashMap::new());
1525                    // Treat const generic parameters as regular inputs
1526                    let param_names = function
1527                        .const_parameters
1528                        .iter()
1529                        .map(|param| param.identifier.name)
1530                        .chain(function.input.iter().map(|input| input.identifier.name));
1531                    for (name, value) in param_names.zip(arguments) {
1532                        self.set_variable(&self.to_absolute_path(&[name]), value);
1533                    }
1534                    self.frames.push(Frame {
1535                        step: 0,
1536                        element: Element::Block { block: function.block.clone(), function_body: true },
1537                        user_initiated: false,
1538                    });
1539                }
1540            }
1541            FunctionVariant::AleoClosure(closure) => {
1542                self.contexts.push(function_path, function_program, self.signer.clone(), false, HashMap::new());
1543                let context = AleoContext::Closure(closure);
1544                self.frames.push(Frame {
1545                    step: 0,
1546                    element: Element::AleoExecution {
1547                        context: context.into(),
1548                        registers: arguments.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1549                        instruction_index: 0,
1550                    },
1551                    user_initiated: false,
1552                });
1553            }
1554            FunctionVariant::AleoFunction(function) => {
1555                let caller = self.new_caller();
1556                self.contexts.push(function_path, function_program, caller, false, HashMap::new());
1557                let context = if finalize {
1558                    let Some(finalize_f) = function.finalize_logic() else {
1559                        panic!("finalize call with no finalize logic");
1560                    };
1561                    AleoContext::Finalize(finalize_f.clone())
1562                } else {
1563                    AleoContext::Function(function)
1564                };
1565                self.frames.push(Frame {
1566                    step: 0,
1567                    element: Element::AleoExecution {
1568                        context: context.into(),
1569                        registers: arguments.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1570                        instruction_index: 0,
1571                    },
1572                    user_initiated: false,
1573                });
1574            }
1575        }
1576
1577        Ok(())
1578    }
1579}
1580
1581#[derive(Clone, Debug)]
1582pub struct StepResult {
1583    /// Has this element completely finished running?
1584    pub finished: bool,
1585
1586    /// If the element was an expression, here's its value.
1587    pub value: Option<Value>,
1588}