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