/ interpreter / src / cursor.rs
cursor.rs
   1  // Copyright (C) 2019-2025 ADnet Contributors
   2  // This file is part of the ADL library.
   3  
   4  // The ADL 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 ADL 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 ADL library. If not, see <https://www.gnu.org/licenses/>.
  16  
  17  use super::*;
  18  
  19  use adl_ast::{
  20      ArrayType,
  21      AssertVariant,
  22      AsyncExpression,
  23      BinaryOperation,
  24      Block,
  25      CompositeFieldInitializer,
  26      DefinitionPlace,
  27      Expression,
  28      Function,
  29      Intrinsic,
  30      Location,
  31      NodeID,
  32      Statement,
  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  };
  46  use adl_errors::{InterpreterHalt, Result};
  47  use adl_span::{Span, Symbol};
  48  
  49  use 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  
  62  use indexmap::IndexMap;
  63  use itertools::Itertools;
  64  use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
  65  use std::{cmp::Ordering, collections::HashMap, mem, str::FromStr as _};
  66  
  67  pub type Closure = SvmClosure<TestnetV0>;
  68  pub type Finalize = SvmFinalize<TestnetV0>;
  69  pub type SvmFunction = SvmFunctionParam<TestnetV0>;
  70  
  71  /// Names associated to values in a function being executed.
  72  #[derive(Clone, Debug)]
  73  pub 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)]
  84  pub struct ContextStack {
  85      contexts: Vec<FunctionContext>,
  86      current_len: usize,
  87  }
  88  
  89  impl 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)]
 170  pub enum AleoContext {
 171      Closure(Closure),
 172      Function(SvmFunction),
 173      Finalize(Finalize),
 174  }
 175  
 176  /// A Leo construct to be evauated.
 177  #[derive(Clone, Debug)]
 178  pub 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  
 213  impl 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)]
 228  pub struct Frame {
 229      pub step: usize,
 230      pub element: Element,
 231      pub user_initiated: bool,
 232  }
 233  
 234  #[derive(Clone, Debug)]
 235  pub 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)]
 243  pub 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 composites: 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  
 294  impl 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  
 336  impl 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              composites: 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::Composite(..)
 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 adl_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 = adl_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 adl_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 adl_value, &mut places.into_iter().rev(), &mut indices.into_iter())?;
 489          self.set_variable(&full_name, adl_value);
 490          Ok(())
 491      }
 492  
 493      pub fn set_program(&mut self, program: &str) {
 494          let p = program.strip_suffix(".alpha").or_else(|| program.strip_suffix(".delta")).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!("{}.alpha", 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                          adl_ast::Expression::ArrayAccess(access) => {
 713                              push(&access.index, &None);
 714                              place = &access.array;
 715                          }
 716                          adl_ast::Expression::Path(..) => break,
 717                          adl_ast::Expression::MemberAccess(access) => {
 718                              place = &access.inner;
 719                          }
 720                          adl_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                          adl_ast::Expression::ArrayAccess(access) => {
 735                              index_count += 1;
 736                              place = &access.array;
 737                          }
 738                          adl_ast::Expression::Path(..) => break,
 739                          adl_ast::Expression::MemberAccess(access) => {
 740                              place = &access.inner;
 741                          }
 742                          adl_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(adl_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 composite member access.
 954                  _ if step == 0 => {
 955                      push!()(&access.inner, &None);
 956                      None
 957                  }
 958                  _ if step == 1 => {
 959                      let composite = self.values.pop().expect_tc(access.span())?;
 960                      let value = composite.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(adl_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 intrinsic = if intr.name == Symbol::intern("__unresolved_get") {
1013                      Intrinsic::MappingGet
1014                  } else if intr.name == Symbol::intern("__unresolved_set") {
1015                      Intrinsic::MappingSet
1016                  } else if let Some(intrinsic) = Intrinsic::from_symbol(intr.name, &intr.type_parameters) {
1017                      intrinsic
1018                  } else {
1019                      halt!(intr.span(), "Unknown intrinsic {}", intr.name);
1020                  };
1021  
1022                  // We want to push expressions for each of the arguments... except for mappings,
1023                  // because we don't look them up as Values.
1024                  match intrinsic {
1025                      Intrinsic::MappingGet | Intrinsic::MappingRemove | Intrinsic::MappingContains => {
1026                          push!()(&intr.arguments[1], &None);
1027                          None
1028                      }
1029                      Intrinsic::MappingGetOrUse | Intrinsic::MappingSet => {
1030                          push!()(&intr.arguments[2], &None);
1031                          push!()(&intr.arguments[1], &None);
1032                          None
1033                      }
1034                      Intrinsic::GroupGen => Some(Value::generator()),
1035                      Intrinsic::SelfSigner => Some(self.signer.clone()),
1036                      Intrinsic::SelfCaller => {
1037                          if let Some(function_context) = self.contexts.last() {
1038                              Some(function_context.caller.clone())
1039                          } else {
1040                              Some(self.signer.clone())
1041                          }
1042                      }
1043                      Intrinsic::SelfAddress => {
1044                          // A helper function to convert a program ID string to an address value.
1045                          fn program_to_address<N: Network>(program_id: &str) -> Result<Value> {
1046                              let Ok(program_id) = ProgramID::<N>::from_str(&format!("{program_id}.alpha")) else {
1047                                  halt_no_span!("Failed to parse program ID");
1048                              };
1049                              let Ok(address) = program_id.to_address() else {
1050                                  halt_no_span!("Failed to convert program ID to address");
1051                              };
1052                              let Ok(value) = Value::from_str(&address.to_string()) else {
1053                                  halt_no_span!("Failed to convert address to value");
1054                              };
1055                              Ok(value)
1056                          }
1057                          // Get the current program.
1058                          let Some(program) = self.current_program() else {
1059                              halt_no_span!("No program context for address");
1060                          };
1061                          #[allow(deprecated)]
1062                          let result = match self.network {
1063                              // ALPHA networks
1064                              NetworkName::AlphaTestnetV0 => program_to_address::<TestnetV0>(&program.to_string())?,
1065                              NetworkName::AlphaMainnetV0 => program_to_address::<MainnetV0>(&program.to_string())?,
1066                              NetworkName::AlphaCanaryV0 => program_to_address::<CanaryV0>(&program.to_string())?,
1067                              // DELTA networks
1068                              NetworkName::DeltaTestnetV0 => program_to_address::<TestnetV0>(&program.to_string())?,
1069                              NetworkName::DeltaMainnetV0 => program_to_address::<MainnetV0>(&program.to_string())?,
1070                              NetworkName::DeltaCanaryV0 => program_to_address::<CanaryV0>(&program.to_string())?,
1071                              // Legacy Aleo networks (deprecated)
1072                              NetworkName::TestnetV0 => program_to_address::<TestnetV0>(&program.to_string())?,
1073                              NetworkName::MainnetV0 => program_to_address::<MainnetV0>(&program.to_string())?,
1074                              NetworkName::CanaryV0 => program_to_address::<CanaryV0>(&program.to_string())?,
1075                          };
1076                          Some(result)
1077                      }
1078                      Intrinsic::BlockHeight => Some(self.block_height.into()),
1079                      Intrinsic::BlockTimestamp => Some(self.block_timestamp.into()),
1080                      Intrinsic::CheatCodePrintMapping => {
1081                          // Do nothing, as we don't need to evaluate the mapping.
1082                          None
1083                      }
1084                      _ => {
1085                          intr.arguments.iter().rev().for_each(|arg| push!()(arg, &None));
1086                          None
1087                      }
1088                  }
1089              }
1090              Expression::Intrinsic(intr) if step == 1 => {
1091                  let intrinsic = if intr.name == Symbol::intern("__unresolved_get") {
1092                      Intrinsic::MappingGet
1093                  } else if intr.name == Symbol::intern("__unresolved_set") {
1094                      Intrinsic::MappingSet
1095                  } else if let Some(intrinsic) = Intrinsic::from_symbol(intr.name, &intr.type_parameters) {
1096                      intrinsic
1097                  } else {
1098                      halt!(intr.span(), "Unknown intrinsic {}", intr.name);
1099                  };
1100  
1101                  let span = intr.span();
1102  
1103                  if let Intrinsic::FutureAwait = intrinsic {
1104                      let value = self.pop_value()?;
1105                      let Some(asyncs) = value.as_future() else {
1106                          halt!(span, "Invalid value for await: {value}");
1107                      };
1108                      for async_execution in asyncs {
1109                          match async_execution {
1110                              AsyncExecution::AsyncFunctionCall { function, arguments } => {
1111                                  self.values.extend(arguments.iter().cloned());
1112                                  self.frames.push(Frame {
1113                                      step: 0,
1114                                      element: Element::DelayedCall(function.clone()),
1115                                      user_initiated: false,
1116                                  });
1117                              }
1118                              AsyncExecution::AsyncBlock { containing_function, block, names, .. } => {
1119                                  self.frames.push(Frame {
1120                                      step: 0,
1121                                      element: Element::DelayedAsyncBlock {
1122                                          program: containing_function.program,
1123                                          block: *block,
1124                                          // Keep track of all the known variables up to this point.
1125                                          // These are available to use inside the block when we actually execute it.
1126                                          names: names.clone().into_iter().collect(),
1127                                      },
1128                                      user_initiated: false,
1129                                  });
1130                              }
1131                          }
1132                      }
1133                      // For an await, we have one extra step - first we must evaluate the delayed call.
1134                      None
1135                  } else {
1136                      let value = evaluate_intrinsic(self, intrinsic.clone(), &intr.arguments, span)?;
1137                      assert!(value.is_some());
1138                      value
1139                  }
1140              }
1141              Expression::Intrinsic(intr) if step == 2 => {
1142                  let intrinsic = if intr.name == Symbol::intern("__unresolved_get") {
1143                      Intrinsic::MappingGet
1144                  } else if intr.name == Symbol::intern("__unresolved_set") {
1145                      Intrinsic::MappingSet
1146                  } else if let Some(intrinsic) = Intrinsic::from_symbol(intr.name, &intr.type_parameters) {
1147                      intrinsic
1148                  } else {
1149                      halt!(intr.span(), "Unknown intrinsic {}", intr.name);
1150                  };
1151                  assert!(intrinsic == Intrinsic::FutureAwait);
1152                  Some(Value::make_unit())
1153              }
1154  
1155              Expression::Binary(binary) if step == 0 => {
1156                  use BinaryOperation::*;
1157  
1158                  // Determine the expected types for the right and left operands based on the operation
1159                  let (right_ty, left_ty) = match binary.op {
1160                      // Multiplications that return a `Group` can take `Scalar * Group` or `Group * Scalar`.
1161                      // No way to know at this stage.
1162                      Mul if matches!(expected_ty, Some(Type::Group)) => (None, None),
1163  
1164                      // Boolean operations don't require expected type propagation
1165                      And | Or | Nand | Nor | Eq | Neq | Lt | Gt | Lte | Gte => (None, None),
1166  
1167                      // Exponentiation (Pow) may require specific typing if expected to be a Field
1168                      Pow => {
1169                          let right_ty = if matches!(expected_ty, Some(Type::Field)) {
1170                              Some(Type::Field) // Enforce Field type on exponent if expected
1171                          } else {
1172                              None // Otherwise, don't constrain the exponent
1173                          };
1174                          (right_ty, expected_ty.clone()) // Pass the expected type to the base
1175                      }
1176  
1177                      // Bitwise shifts and wrapped exponentiation:
1178                      // Typically only the left operand should conform to the expected type
1179                      Shl | ShlWrapped | Shr | ShrWrapped | PowWrapped => (None, expected_ty.clone()),
1180  
1181                      // Default case: propagate expected type to both operands
1182                      _ => (expected_ty.clone(), expected_ty.clone()),
1183                  };
1184  
1185                  // Push operands onto the stack for evaluation in right-to-left order
1186                  push!()(&binary.right, &right_ty);
1187                  push!()(&binary.left, &left_ty);
1188  
1189                  None
1190              }
1191              Expression::Binary(binary) if step == 1 => {
1192                  let rhs = self.pop_value()?;
1193                  let lhs = self.pop_value()?;
1194                  Some(evaluate_binary(binary.span, binary.op, &lhs, &rhs, expected_ty)?)
1195              }
1196  
1197              Expression::Call(call) if step == 0 => {
1198                  // Resolve the function's program and name
1199                  let (function_program, function_path) = {
1200                      let maybe_program = call.program.or_else(|| self.current_program());
1201                      if let Some(program) = maybe_program {
1202                          (program, self.to_absolute_path(&call.function.as_symbols()))
1203                      } else {
1204                          halt!(call.span, "No current program");
1205                      }
1206                  };
1207  
1208                  // Look up the function variant (Leo, AleoClosure, or AleoFunction)
1209                  let Some(function_variant) = self.lookup_function(function_program, &function_path) else {
1210                      halt!(call.span, "unknown function {function_program}.alpha/{}", function_path.iter().format("::"));
1211                  };
1212  
1213                  // Extract const parameter and input types based on the function variant
1214                  let (const_param_types, input_types) = match function_variant {
1215                      FunctionVariant::Leo(function) => (
1216                          function.const_parameters.iter().map(|p| p.type_.clone()).collect::<Vec<_>>(),
1217                          function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>(),
1218                      ),
1219                      FunctionVariant::AleoClosure(closure) => {
1220                          let function = adl_ast::FunctionStub::from_closure(&closure, function_program);
1221                          (vec![], function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>())
1222                      }
1223                      FunctionVariant::AleoFunction(svm_function) => {
1224                          let function = adl_ast::FunctionStub::from_function_core(&svm_function, function_program);
1225                          (vec![], function.input.iter().map(|p| p.type_.clone()).collect::<Vec<_>>())
1226                      }
1227                  };
1228  
1229                  // Push arguments (in reverse order) with corresponding input types
1230                  call.arguments
1231                      .iter()
1232                      .rev()
1233                      .zip(input_types.iter().rev())
1234                      .for_each(|(arg, ty)| push!()(arg, &Some(ty.clone())));
1235  
1236                  // Push const arguments (in reverse order) with corresponding const param types
1237                  call.const_arguments
1238                      .iter()
1239                      .rev()
1240                      .zip(const_param_types.iter().rev())
1241                      .for_each(|(arg, ty)| push!()(arg, &Some(ty.clone())));
1242  
1243                  None
1244              }
1245  
1246              Expression::Call(call) if step == 1 => {
1247                  let len = self.values.len();
1248                  let (program, path) = {
1249                      let maybe_program = call.program.or_else(|| self.current_program());
1250                      if let Some(program) = maybe_program {
1251                          (program, call.function.as_symbols())
1252                      } else {
1253                          halt!(call.span, "No current program");
1254                      }
1255                  };
1256                  // It's a bit cheesy to collect the arguments into a Vec first, but it's the easiest way
1257                  // to handle lifetimes here.
1258                  let arguments: Vec<Value> =
1259                      self.values.drain(len - call.arguments.len() - call.const_arguments.len()..).collect();
1260                  self.do_call(
1261                      program,
1262                      &self.to_absolute_path(&path),
1263                      arguments.into_iter(),
1264                      false, // finalize
1265                      call.span(),
1266                  )?;
1267                  None
1268              }
1269              Expression::Call(_call) if step == 2 => Some(self.pop_value()?),
1270              Expression::Cast(cast) if step == 0 => {
1271                  push!()(&cast.expression, &None);
1272                  None
1273              }
1274              Expression::Cast(cast) if step == 1 => {
1275                  let span = cast.span();
1276                  let arg = self.pop_value()?;
1277                  match arg.cast(&cast.type_) {
1278                      Some(value) => Some(value),
1279                      None => return Err(InterpreterHalt::new_spanned("cast failure".to_string(), span).into()),
1280                  }
1281              }
1282              Expression::Err(_) => todo!(),
1283              Expression::Path(path) if step == 0 => {
1284                  Some(self.lookup(&self.to_absolute_path(&path.as_symbols())).expect_tc(path.span())?)
1285              }
1286              Expression::Literal(literal) if step == 0 => Some(literal_to_value(literal, expected_ty)?),
1287              Expression::Locator(_locator) => todo!(),
1288              Expression::Composite(composite) if step == 0 => {
1289                  let members = self
1290                      .composites
1291                      .get(&self.to_absolute_path(&composite.path.as_symbols()))
1292                      .expect_tc(composite.span())?;
1293                  for CompositeFieldInitializer { identifier: field_init_name, expression: init, .. } in
1294                      &composite.members
1295                  {
1296                      let Some(type_) = members.get(&field_init_name.name) else { tc_fail!() };
1297                      push!()(
1298                          init.as_ref().unwrap_or(&Expression::Path(Path::from(*field_init_name))),
1299                          &Some(type_.clone()),
1300                      )
1301                  }
1302  
1303                  None
1304              }
1305              Expression::Composite(composite) if step == 1 => {
1306                  // Collect all the key/value pairs into a HashMap.
1307                  let mut contents_tmp = HashMap::with_capacity(composite.members.len());
1308                  for initializer in composite.members.iter() {
1309                      let name = initializer.identifier.name;
1310                      let value = self.pop_value()?;
1311                      contents_tmp.insert(name, value);
1312                  }
1313  
1314                  // And now put them into an IndexMap in the correct order.
1315                  let members = self
1316                      .composites
1317                      .get(&self.to_absolute_path(&composite.path.as_symbols()))
1318                      .expect_tc(composite.span())?;
1319                  let contents = members.iter().map(|(identifier, _)| {
1320                      (*identifier, contents_tmp.remove(identifier).expect("we just inserted this"))
1321                  });
1322  
1323                  // TODO: this only works for composites defined in the top level module.. must figure
1324                  // something out for structs defined in modules
1325                  Some(Value::make_struct(contents, self.current_program().unwrap(), composite.path.as_symbols()))
1326              }
1327              Expression::Ternary(ternary) if step == 0 => {
1328                  push!()(&ternary.condition, &None);
1329                  None
1330              }
1331              Expression::Ternary(ternary) if step == 1 => {
1332                  let condition = self.pop_value()?;
1333                  match condition.try_into() {
1334                      Ok(true) => push!()(&ternary.if_true, &None),
1335                      Ok(false) => push!()(&ternary.if_false, &None),
1336                      _ => halt!(ternary.span(), "Invalid type for ternary expression {ternary}"),
1337                  }
1338                  None
1339              }
1340              Expression::Ternary(_) if step == 2 => Some(self.pop_value()?),
1341              Expression::Tuple(tuple) if step == 0 => {
1342                  tuple.elements.iter().rev().for_each(|t| push!()(t, &None));
1343                  None
1344              }
1345              Expression::Tuple(tuple) if step == 1 => {
1346                  let len = self.values.len();
1347                  let tuple_values = self.values.drain(len - tuple.elements.len()..);
1348                  Some(Value::make_tuple(tuple_values))
1349              }
1350              Expression::Unary(unary) if step == 0 => {
1351                  use UnaryOperation::*;
1352  
1353                  // Determine the expected type based on the unary operation
1354                  let ty = match unary.op {
1355                      Inverse | Square | SquareRoot => Some(Type::Field), // These ops require Field operands
1356                      ToXCoordinate | ToYCoordinate => Some(Type::Group), // These ops apply to Group elements
1357                      _ => expected_ty.clone(),                           // Fallback to the externally expected type
1358                  };
1359  
1360                  // Push the receiver expression with the computed type
1361                  push!()(&unary.receiver, &ty);
1362  
1363                  None
1364              }
1365              Expression::Unary(unary) if step == 1 => {
1366                  let value = self.pop_value()?;
1367                  Some(evaluate_unary(unary.span, unary.op, &value, expected_ty)?)
1368              }
1369              Expression::Unit(_) if step == 0 => Some(Value::make_unit()),
1370              x => unreachable!("Unexpected expression {x}"),
1371          } {
1372              assert_eq!(self.frames.len(), len);
1373              self.frames.pop();
1374              self.values.push(value);
1375              Ok(true)
1376          } else {
1377              self.frames[len - 1].step += 1;
1378              Ok(false)
1379          }
1380      }
1381  
1382      /// Execute one step of the current element.
1383      ///
1384      /// Many Leo constructs require multiple steps. For instance, when executing a conditional,
1385      /// the first step will push the condition expression to the stack. Once that has executed
1386      /// and we've returned to the conditional, we push the `then` or `otherwise` block to the
1387      /// stack. Once that has executed and we've returned to the conditional, the final step
1388      /// does nothing.
1389      pub fn step(&mut self) -> Result<StepResult> {
1390          if self.frames.is_empty() {
1391              return Err(InterpreterHalt::new("no execution frames available".into()).into());
1392          }
1393  
1394          let Frame { element, step, user_initiated } = self.frames.last().expect("there should be a frame").clone();
1395          match element {
1396              Element::Block { block, function_body } => {
1397                  let finished = self.step_block(&block, function_body, step);
1398                  Ok(StepResult { finished, value: None })
1399              }
1400              Element::Statement(statement) => {
1401                  let finished = self.step_statement(&statement, step)?;
1402                  Ok(StepResult { finished, value: None })
1403              }
1404              Element::Expression(expression, expected_ty) => {
1405                  let finished = self.step_expression(&expression, &expected_ty, step)?;
1406                  let value = match (finished, user_initiated) {
1407                      (false, _) => None,
1408                      (true, false) => self.values.last().cloned(),
1409                      (true, true) => self.values.pop(),
1410                  };
1411  
1412                  let maybe_future = if let Some(len) = value.as_ref().and_then(|val| val.tuple_len()) {
1413                      value.as_ref().unwrap().tuple_index(len - 1)
1414                  } else {
1415                      value.clone()
1416                  };
1417  
1418                  if let Some(asyncs) = maybe_future.as_ref().and_then(|fut| fut.as_future())
1419                      && user_initiated
1420                  {
1421                      self.futures.extend(asyncs.iter().cloned());
1422                  }
1423  
1424                  Ok(StepResult { finished, value })
1425              }
1426              Element::AleoExecution { .. } => {
1427                  self.step_alpha()?;
1428                  Ok(StepResult { finished: true, value: None })
1429              }
1430              Element::DelayedCall(gid) if step == 0 => {
1431                  match self.lookup_function(gid.program, &gid.path).expect("function should exist") {
1432                      FunctionVariant::Leo(function) => {
1433                          assert!(function.variant == Variant::AsyncFunction);
1434                          let len = self.values.len();
1435                          let values: Vec<Value> = self.values.drain(len - function.input.len()..).collect();
1436                          self.contexts.push(
1437                              &gid.path,
1438                              gid.program,
1439                              self.signer.clone(),
1440                              true, // is_async
1441                              HashMap::new(),
1442                          );
1443                          let param_names = function.input.iter().map(|input| input.identifier.name);
1444                          for (name, value) in param_names.zip(values) {
1445                              self.set_variable(&self.to_absolute_path(&[name]), value);
1446                          }
1447                          self.frames.last_mut().unwrap().step = 1;
1448                          self.frames.push(Frame {
1449                              step: 0,
1450                              element: Element::Block { block: function.block.clone(), function_body: true },
1451                              user_initiated: false,
1452                          });
1453                          Ok(StepResult { finished: false, value: None })
1454                      }
1455                      FunctionVariant::AleoFunction(function) => {
1456                          let Some(finalize_f) = function.finalize_logic() else {
1457                              panic!("must have finalize logic for a delayed call");
1458                          };
1459                          let len = self.values.len();
1460                          let values_iter = self.values.drain(len - finalize_f.inputs().len()..);
1461                          self.contexts.push(
1462                              &gid.path,
1463                              gid.program,
1464                              self.signer.clone(),
1465                              true, // is_async
1466                              HashMap::new(),
1467                          );
1468                          self.frames.last_mut().unwrap().step = 1;
1469                          self.frames.push(Frame {
1470                              step: 0,
1471                              element: Element::AleoExecution {
1472                                  context: AleoContext::Finalize(finalize_f.clone()).into(),
1473                                  registers: values_iter.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1474                                  instruction_index: 0,
1475                              },
1476                              user_initiated: false,
1477                          });
1478                          Ok(StepResult { finished: false, value: None })
1479                      }
1480                      FunctionVariant::AleoClosure(..) => panic!("A call to a closure can't be delayed"),
1481                  }
1482              }
1483              Element::DelayedCall(_gid) => {
1484                  assert_eq!(step, 1);
1485                  let value = self.values.pop();
1486                  self.frames.pop();
1487                  Ok(StepResult { finished: true, value })
1488              }
1489              Element::DelayedAsyncBlock { program, block, names } if step == 0 => {
1490                  self.contexts.push(
1491                      &[Symbol::intern("")],
1492                      program,
1493                      self.signer.clone(),
1494                      true,
1495                      names.clone().into_iter().collect(), // Set the known names to the previously preserved `names`.
1496                  );
1497                  self.frames.last_mut().unwrap().step = 1;
1498                  self.frames.push(Frame {
1499                      step: 0,
1500                      element: Element::Block {
1501                          block: self.async_blocks.get(&block).unwrap().clone(),
1502                          function_body: true,
1503                      },
1504                      user_initiated: false,
1505                  });
1506                  Ok(StepResult { finished: false, value: None })
1507              }
1508              Element::DelayedAsyncBlock { .. } => {
1509                  assert_eq!(step, 1);
1510                  let value = self.values.pop();
1511                  self.frames.pop();
1512                  Ok(StepResult { finished: true, value })
1513              }
1514          }
1515      }
1516  
1517      pub fn do_call(
1518          &mut self,
1519          function_program: Symbol,
1520          function_path: &[Symbol],
1521          arguments: impl Iterator<Item = Value>,
1522          finalize: bool,
1523          span: Span,
1524      ) -> Result<()> {
1525          let Some(function_variant) = self.lookup_function(function_program, function_path) else {
1526              halt!(span, "unknown function {function_program}.alpha/{}", function_path.iter().format("::"));
1527          };
1528          match function_variant {
1529              FunctionVariant::Leo(function) => {
1530                  let caller = if matches!(function.variant, Variant::Transition | Variant::AsyncTransition) {
1531                      self.new_caller()
1532                  } else {
1533                      self.signer.clone()
1534                  };
1535                  if self.really_async && function.variant == Variant::AsyncFunction {
1536                      // Don't actually run the call now.
1537                      let async_ex = AsyncExecution::AsyncFunctionCall {
1538                          function: Location::new(function_program, function_path.to_vec()),
1539                          arguments: arguments.collect(),
1540                      };
1541                      self.values.push(vec![async_ex].into());
1542                  } else {
1543                      let is_async = function.variant == Variant::AsyncFunction;
1544                      self.contexts.push(function_path, function_program, caller, is_async, HashMap::new());
1545                      // Treat const generic parameters as regular inputs
1546                      let param_names = function
1547                          .const_parameters
1548                          .iter()
1549                          .map(|param| param.identifier.name)
1550                          .chain(function.input.iter().map(|input| input.identifier.name));
1551                      for (name, value) in param_names.zip(arguments) {
1552                          self.set_variable(&self.to_absolute_path(&[name]), value);
1553                      }
1554                      self.frames.push(Frame {
1555                          step: 0,
1556                          element: Element::Block { block: function.block.clone(), function_body: true },
1557                          user_initiated: false,
1558                      });
1559                  }
1560              }
1561              FunctionVariant::AleoClosure(closure) => {
1562                  self.contexts.push(function_path, function_program, self.signer.clone(), false, HashMap::new());
1563                  let context = AleoContext::Closure(closure);
1564                  self.frames.push(Frame {
1565                      step: 0,
1566                      element: Element::AleoExecution {
1567                          context: context.into(),
1568                          registers: arguments.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1569                          instruction_index: 0,
1570                      },
1571                      user_initiated: false,
1572                  });
1573              }
1574              FunctionVariant::AleoFunction(function) => {
1575                  let caller = self.new_caller();
1576                  self.contexts.push(function_path, function_program, caller, false, HashMap::new());
1577                  let context = if finalize {
1578                      let Some(finalize_f) = function.finalize_logic() else {
1579                          panic!("finalize call with no finalize logic");
1580                      };
1581                      AleoContext::Finalize(finalize_f.clone())
1582                  } else {
1583                      AleoContext::Function(function)
1584                  };
1585                  self.frames.push(Frame {
1586                      step: 0,
1587                      element: Element::AleoExecution {
1588                          context: context.into(),
1589                          registers: arguments.enumerate().map(|(i, v)| (i as u64, v)).collect(),
1590                          instruction_index: 0,
1591                      },
1592                      user_initiated: false,
1593                  });
1594              }
1595          }
1596  
1597          Ok(())
1598      }
1599  }
1600  
1601  #[derive(Clone, Debug)]
1602  pub struct StepResult {
1603      /// Has this element completely finished running?
1604      pub finished: bool,
1605  
1606      /// If the element was an expression, here's its value.
1607      pub value: Option<Value>,
1608  }