diff --git a/crates/nargo/src/cli/mod.rs b/crates/nargo/src/cli/mod.rs index 269acbfbf6a..336dffecc09 100644 --- a/crates/nargo/src/cli/mod.rs +++ b/crates/nargo/src/cli/mod.rs @@ -1,3 +1,4 @@ +use acvm::{acir::circuit::PublicInputs, FieldElement}; pub use check_cmd::check_from_path; use clap::{App, AppSettings, Arg}; use const_format::formatcp; @@ -9,7 +10,7 @@ use noirc_abi::{ use noirc_driver::Driver; use noirc_frontend::graph::{CrateName, CrateType}; use std::{ - collections::BTreeMap, + collections::{BTreeMap, HashMap, HashSet}, fs::File, io::Write, path::{Path, PathBuf}, @@ -206,6 +207,44 @@ fn path_to_stdlib() -> PathBuf { dirs::config_dir().unwrap().join("noir-lang").join("std/src") } +// Removes duplicates from the list of public input witnesses +fn dedup_public_input_indices(indices: PublicInputs) -> PublicInputs { + let duplicates_removed: HashSet<_> = indices.0.into_iter().collect(); + PublicInputs(duplicates_removed.into_iter().collect()) +} + +// Removes duplicates from the list of public input witnesses and the +// associated list of duplicate values. +pub(crate) fn dedup_public_input_indices_values( + indices: PublicInputs, + values: Vec, +) -> (PublicInputs, Vec) { + // Assume that the public input index lists and the values contain duplicates + assert_eq!(indices.0.len(), values.len()); + + let mut public_inputs_without_duplicates = Vec::new(); + let mut already_seen_public_indices = HashMap::new(); + + for (index, value) in indices.0.iter().zip(values) { + match already_seen_public_indices.get(index) { + Some(expected_value) => { + // The index has already been added + // so lets check that the values already inserted is equal to the value, we wish to insert + assert_eq!(*expected_value, value, "witness index {index:?} does not have a canonical map. The expected value is {expected_value}, the received value is {value}.") + } + None => { + already_seen_public_indices.insert(*index, value); + public_inputs_without_duplicates.push(value) + } + } + } + + ( + PublicInputs(already_seen_public_indices.keys().copied().collect()), + public_inputs_without_duplicates, + ) +} + // FIXME: I not sure that this is the right place for this tests. #[cfg(test)] mod tests { diff --git a/crates/nargo/src/cli/prove_cmd.rs b/crates/nargo/src/cli/prove_cmd.rs index f9624ecc751..a1fda13b3db 100644 --- a/crates/nargo/src/cli/prove_cmd.rs +++ b/crates/nargo/src/cli/prove_cmd.rs @@ -1,5 +1,3 @@ -use std::{collections::BTreeMap, path::PathBuf}; - use acvm::acir::native_types::Witness; use acvm::FieldElement; use acvm::PartialWitnessGenerator; @@ -7,9 +5,13 @@ use acvm::ProofSystemCompiler; use clap::ArgMatches; use noirc_abi::errors::AbiError; use noirc_abi::input_parser::{Format, InputValue}; -use std::path::Path; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; use super::{create_named_dir, read_inputs_from_file, write_inputs_to_file, write_to_file}; +use crate::cli::dedup_public_input_indices; use crate::{ constants::{PROOFS_DIR, PROOF_EXT, PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}, errors::CliError, @@ -69,12 +71,20 @@ pub fn compile_circuit_and_witness>( show_ssa: bool, allow_unused_variables: bool, ) -> Result<(noirc_driver::CompiledProgram, BTreeMap), CliError> { - let compiled_program = super::compile_cmd::compile_circuit( + let mut compiled_program = super::compile_cmd::compile_circuit( program_dir.as_ref(), show_ssa, allow_unused_variables, )?; let solved_witness = parse_and_solve_witness(program_dir, &compiled_program)?; + + // Since the public outputs are added into the public inputs list + // There can be duplicates. We keep the duplicates for when one is + // encoding the return values into the Verifier.toml + // However, for creating a proof, we remove these duplicates. + compiled_program.circuit.public_inputs = + dedup_public_input_indices(compiled_program.circuit.public_inputs); + Ok((compiled_program, solved_witness)) } diff --git a/crates/nargo/src/cli/verify_cmd.rs b/crates/nargo/src/cli/verify_cmd.rs index 30d184d92e4..8ec38531102 100644 --- a/crates/nargo/src/cli/verify_cmd.rs +++ b/crates/nargo/src/cli/verify_cmd.rs @@ -1,4 +1,6 @@ -use super::{compile_cmd::compile_circuit, read_inputs_from_file}; +use super::{ + compile_cmd::compile_circuit, dedup_public_input_indices_values, read_inputs_from_file, +}; use crate::{ constants::{PROOFS_DIR, PROOF_EXT, VERIFIER_INPUT_FILE}, errors::CliError, @@ -59,7 +61,7 @@ pub fn verify_with_path>( } fn verify_proof( - compiled_program: CompiledProgram, + mut compiled_program: CompiledProgram, public_inputs: BTreeMap, proof: Vec, ) -> Result { @@ -71,8 +73,14 @@ fn verify_proof( _ => CliError::from(error), })?; + // Similarly to when proving -- we must remove the duplicate public witnesses which + // can be present because a public input can also be added as a public output. + let (dedup_public_indices, dedup_public_values) = + dedup_public_input_indices_values(compiled_program.circuit.public_inputs, public_inputs); + compiled_program.circuit.public_inputs = dedup_public_indices; + let backend = crate::backends::ConcreteBackend; - let valid_proof = backend.verify_from_cs(&proof, public_inputs, compiled_program.circuit); + let valid_proof = backend.verify_from_cs(&proof, dedup_public_values, compiled_program.circuit); Ok(valid_proof) } diff --git a/crates/nargo/tests/test_data/hash_to_field/Prover.toml b/crates/nargo/tests/test_data/hash_to_field/Prover.toml index 079763a108e..f6597d3f78a 100644 --- a/crates/nargo/tests/test_data/hash_to_field/Prover.toml +++ b/crates/nargo/tests/test_data/hash_to_field/Prover.toml @@ -1,2 +1 @@ input = "1" -return = "0x25cebc29ded2fa515a937e2b5f674e3026c012e5b57f8a48d7dce6b7d274f9d9" diff --git a/crates/nargo/tests/test_data/higher-order-functions/Prover.toml b/crates/nargo/tests/test_data/higher-order-functions/Prover.toml index d3504ebe83f..8b137891791 100644 --- a/crates/nargo/tests/test_data/higher-order-functions/Prover.toml +++ b/crates/nargo/tests/test_data/higher-order-functions/Prover.toml @@ -1 +1 @@ -return = "5" \ No newline at end of file + diff --git a/crates/nargo/tests/test_data/main_return/Prover.toml b/crates/nargo/tests/test_data/main_return/Prover.toml index ef1ca7bb7e8..63e9878811a 100644 --- a/crates/nargo/tests/test_data/main_return/Prover.toml +++ b/crates/nargo/tests/test_data/main_return/Prover.toml @@ -1,2 +1 @@ -return = "" x = "8" diff --git a/crates/nargo/tests/test_data/main_return/src/main.nr b/crates/nargo/tests/test_data/main_return/src/main.nr index aefa2532731..06347eb0919 100644 --- a/crates/nargo/tests/test_data/main_return/src/main.nr +++ b/crates/nargo/tests/test_data/main_return/src/main.nr @@ -1,3 +1,3 @@ -fn main(x: Field) -> pub Field { +fn main(x: pub Field) -> pub Field { x } diff --git a/crates/nargo/tests/test_data/modulus/Prover.toml b/crates/nargo/tests/test_data/modulus/Prover.toml index d46300b8a5b..d435609bb1a 100644 --- a/crates/nargo/tests/test_data/modulus/Prover.toml +++ b/crates/nargo/tests/test_data/modulus/Prover.toml @@ -1,3 +1,290 @@ -bn254_modulus_be_bytes = [48, 100, 78, 114, 225, 49, 160, 41, 184, 80, 69, 182, 129, 129, 88, 93, 40, 51, 232, 72, 121, 185, 112, 145, 67, 225, 245, 147, 240, 0, 0, 1] -bn254_modulus_be_bits = [1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] -return = "" \ No newline at end of file +bn254_modulus_be_bytes = [ + 48, + 100, + 78, + 114, + 225, + 49, + 160, + 41, + 184, + 80, + 69, + 182, + 129, + 129, + 88, + 93, + 40, + 51, + 232, + 72, + 121, + 185, + 112, + 145, + 67, + 225, + 245, + 147, + 240, + 0, + 0, + 1, +] +bn254_modulus_be_bits = [ + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, +] diff --git a/crates/nargo/tests/test_data/simple_shield/Prover.toml b/crates/nargo/tests/test_data/simple_shield/Prover.toml index 4c03ef25a18..67e825f6333 100644 --- a/crates/nargo/tests/test_data/simple_shield/Prover.toml +++ b/crates/nargo/tests/test_data/simple_shield/Prover.toml @@ -5,11 +5,7 @@ index = "0" note_hash_path = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", - "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40", ] to_pubkey_x = "0x0000000000000000000000000000000000000000000000000000000000000001" to_pubkey_y = "0x0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c" -return = [ - "0x1fa077bf4c787f858e0f3a7fa031ff11f200a0ccc6e549c6af98ec96800af340", - "0x0c25956c07e2277e75f1ca10bae32c29f24b27f25625fe9258cec29dc90651dd" -] \ No newline at end of file diff --git a/crates/nargo/tests/test_data/struct_inputs/Prover.toml b/crates/nargo/tests/test_data/struct_inputs/Prover.toml index 46aae99b425..ca992f8502c 100644 --- a/crates/nargo/tests/test_data/struct_inputs/Prover.toml +++ b/crates/nargo/tests/test_data/struct_inputs/Prover.toml @@ -1,5 +1,4 @@ x = "5" -return = "" [y] foo = "5" @@ -15,4 +14,4 @@ message = "helld" [a.bar_struct] val = "1" array = [0, 1] -message = "hello" \ No newline at end of file +message = "hello" diff --git a/crates/nargo/tests/test_data/to_le_bytes/Prover.toml b/crates/nargo/tests/test_data/to_le_bytes/Prover.toml index f5f4f367d1c..07fe857ac7c 100644 --- a/crates/nargo/tests/test_data/to_le_bytes/Prover.toml +++ b/crates/nargo/tests/test_data/to_le_bytes/Prover.toml @@ -1,2 +1 @@ x = "2040124" -return = [0x3c, 0x21, 0x1f, 0x00] \ No newline at end of file diff --git a/crates/noirc_abi/src/lib.rs b/crates/noirc_abi/src/lib.rs index dda568472e7..821cb663912 100644 --- a/crates/noirc_abi/src/lib.rs +++ b/crates/noirc_abi/src/lib.rs @@ -155,12 +155,24 @@ impl Abi { pub fn encode( self, inputs: &BTreeMap, - allow_undefined_return: bool, + skip_output: bool, ) -> Result, AbiError> { - let param_names = self.parameter_names(); + // Condition that specifies whether we should filter the "return" + // parameter. We do this in the case that it is not in the `inputs` + // map specified. + // + // See Issue #645 : Adding a `public outputs` field into acir and + // the ABI will clean up this logic + // For prosperity; the prover does not know about a `return` value + // so we skip this when encoding the ABI + let return_condition = + |param_name: &&String| !skip_output || (param_name != &MAIN_RETURN_NAME); + + let parameters = self.parameters.iter().filter(|param| return_condition(&¶m.name)); + let param_names: Vec<&String> = parameters.clone().map(|param| ¶m.name).collect(); let mut encoded_inputs = Vec::new(); - for param in self.parameters.iter() { + for param in parameters { let value = inputs .get(¶m.name) .ok_or_else(|| AbiError::MissingParam(param.name.to_owned()))? @@ -170,26 +182,6 @@ impl Abi { return Err(AbiError::TypeMismatch { param: param.to_owned(), value }); } - // As the circuit calculates the return value in the process of calculating rest of the witnesses - // it's not absolutely necessary to provide them as inputs. We then tolerate an undefined value for - // the return value input and just skip it. - if allow_undefined_return - && param.name == MAIN_RETURN_NAME - && matches!(value, InputValue::Undefined) - { - let return_witness_len = param.typ.field_count(); - - // We do not support undefined arrays for now - TODO - if return_witness_len != 1 { - return Err(AbiError::Generic( - "Values of array returned from main must be specified".to_string(), - )); - } else { - // This assumes that the return value is at the end of the ABI, otherwise values will be misaligned. - continue; - } - } - encoded_inputs.extend(Self::encode_value(value, ¶m.name)?); } @@ -240,7 +232,6 @@ impl Abi { let mut field_iterator = encoded_inputs.iter().cloned(); let mut decoded_inputs = BTreeMap::new(); - for param in &self.parameters { let decoded_value = Self::decode_value(&mut field_iterator, ¶m.typ)?; diff --git a/crates/noirc_evaluator/src/lib.rs b/crates/noirc_evaluator/src/lib.rs index ba98393f7cd..3e07c6ae7c1 100644 --- a/crates/noirc_evaluator/src/lib.rs +++ b/crates/noirc_evaluator/src/lib.rs @@ -20,6 +20,9 @@ pub struct Evaluator { // so it is safer to use a u32, at least until clang is changed // to compile wasm64. current_witness_index: u32, + // This is the number of witnesses indices used when + // creating the private/public inputs of the ABI. + num_witnesses_abi_len: usize, public_inputs: Vec, opcodes: Vec, } @@ -60,6 +63,7 @@ impl Evaluator { fn new() -> Self { Evaluator { public_inputs: Vec::new(), + num_witnesses_abi_len: 0, // XXX: Barretenberg, reserves the first index to have value 0. // When we increment, we do not use this index at all. // This means that every constraint system at the moment, will either need @@ -73,6 +77,22 @@ impl Evaluator { } } + // Returns true if the `witness_index` + // was created in the ABI as a private input. + // + // Note: This method is used so that we don't convert private + // ABI inputs into public outputs. + fn is_private_abi_input(&self, witness_index: Witness) -> bool { + // If the `witness_index` is more than the `num_witnesses_abi_len` + // then it was created after the ABI was processed and is therefore + // an intermediate variable. + let is_intermediate_variable = witness_index.as_usize() > self.num_witnesses_abi_len; + + let is_public_input = self.public_inputs.contains(&witness_index); + + !is_intermediate_variable && !is_public_input + } + // Creates a new Witness index fn add_witness_to_cs(&mut self) -> Witness { self.current_witness_index += 1; @@ -261,6 +281,16 @@ impl Evaluator { let main = igen.program.main(); let main_params = std::mem::take(&mut main.parameters); let abi_params = std::mem::take(&mut igen.program.abi.parameters); + + // Remove the return type from the parameters + // Since this is not in the main functions parameters. + // + // TODO(See Issue633) regarding adding a `return_type` field to the ABI struct + let abi_params: Vec<_> = abi_params + .into_iter() + .filter(|param| param.name != noirc_abi::MAIN_RETURN_NAME) + .collect(); + assert_eq!(main_params.len(), abi_params.len()); for ((param_id, _, param_name, _), abi_param) in main_params.iter().zip(abi_params) { @@ -269,5 +299,9 @@ impl Evaluator { self.param_to_var(param_name, def, &abi_param.typ, &abi_param.visibility, igen) .unwrap(); } + + // Store the number of witnesses used to represent the types + // in the ABI + self.num_witnesses_abi_len = self.current_witness_index as usize; } } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen.rs b/crates/noirc_evaluator/src/ssa/acir_gen.rs index 679bf4de86c..13efd26c211 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen.rs @@ -188,7 +188,46 @@ impl Acir { InternalVar::from(v) } Operation::Call { .. } => unreachable!("call instruction should have been inlined"), - Operation::Return(_) => todo!(), //return from main + Operation::Return(node_ids) => { + // XXX: When we return a node_id that was created from + // the UnitType, there is a witness associated with it + // Ideally no witnesses are created for such types. + + // This can only ever be called in the main context. + // In all other context's, the return operation is transformed. + + for node_id in node_ids { + // An array produces a single node_id + // We therefore need to check if the node_id is referring to an array + // and deference to get the elements + let objects = match Memory::deref(ctx, *node_id) { + Some(a) => { + let array = &ctx.mem[a]; + self.load_array(array, false, evaluator) + } + None => vec![self.substitute(*node_id, evaluator, ctx)], + }; + + for mut object in objects { + let witness = if object.expression.is_const() { + evaluator.create_intermediate_variable(object.expression) + } else { + object.generate_witness(evaluator) + }; + + // Before pushing to the public inputs, we need to check that + // it was not a private ABI input + if evaluator.is_private_abi_input(witness) { + return Err(RuntimeErrorKind::Spanless(String::from( + "we do not allow private ABI inputs to be returned as public outputs", + ))); + } + evaluator.public_inputs.push(witness); + } + } + + InternalVar::default() + } Operation::Cond { condition, val_true: lhs, val_false: rhs } => { let cond = self.substitute(*condition, evaluator, ctx); let l_c = self.substitute(*lhs, evaluator, ctx); diff --git a/crates/noirc_evaluator/src/ssa/code_gen.rs b/crates/noirc_evaluator/src/ssa/code_gen.rs index 0e3df891aec..75a44c5e8f1 100644 --- a/crates/noirc_evaluator/src/ssa/code_gen.rs +++ b/crates/noirc_evaluator/src/ssa/code_gen.rs @@ -126,7 +126,12 @@ impl IRGenerator { pub fn codegen_main(&mut self) -> Result<(), RuntimeError> { let main_body = self.program.take_main_body(); - self.codegen_expression(&main_body)?; + let value = self.codegen_expression(&main_body)?; + let node_ids = value.to_node_ids(); + + if self.program.main().return_type != Type::Unit { + self.context.new_instruction(Operation::Return(node_ids), ObjectType::NotAnObject)?; + } Ok(()) } diff --git a/crates/noirc_evaluator/src/ssa/integer.rs b/crates/noirc_evaluator/src/ssa/integer.rs index ed4945dcbfc..b989f7f427a 100644 --- a/crates/noirc_evaluator/src/ssa/integer.rs +++ b/crates/noirc_evaluator/src/ssa/integer.rs @@ -246,10 +246,7 @@ fn block_overflow( for mut ins in instructions { if matches!( ins.operation, - Operation::Nop - | Operation::Call { .. } - | Operation::Result { .. } - | Operation::Return(_) + Operation::Nop | Operation::Call { .. } | Operation::Result { .. } ) { //For now we skip completely functions from overflow; that means arguments are NOT truncated. //The reasoning is that this is handled by doing the overflow strategy after the function has been inlined @@ -481,7 +478,7 @@ fn get_max_value(ins: &Instruction, max_map: &mut HashMap) -> B Operation::Load { .. } => unreachable!(), Operation::Store { .. } => BigUint::zero(), Operation::Call { .. } => ins.res_type.max_size(), //n.b. functions should have been inlined - Operation::Return(_) => todo!(), + Operation::Return(_) => ins.res_type.max_size(), Operation::Result { .. } => { unreachable!("Functions must have been inlined before checking for overflows") } diff --git a/crates/noirc_frontend/src/monomorphisation/mod.rs b/crates/noirc_frontend/src/monomorphisation/mod.rs index d20a520c4f6..746e1203fd1 100644 --- a/crates/noirc_frontend/src/monomorphisation/mod.rs +++ b/crates/noirc_frontend/src/monomorphisation/mod.rs @@ -129,49 +129,12 @@ impl Monomorphiser { self.globals.entry(id).or_default().insert(typ, new_id); } - /// The main function is special, we need to check for a return type and if present, - /// insert an extra constrain on the return value. fn compile_main(&mut self, main_id: node_interner::FuncId) -> Abi { let new_main_id = self.next_function_id(); assert_eq!(new_main_id, Program::main_id()); self.function(main_id, new_main_id); let main_meta = self.interner.function_meta(&main_id); - let main = self.finished_functions.get_mut(&new_main_id).unwrap(); - - // If the main function has a return type we manually desugar it here - // to add a `constrain ret == expected_ret` where `ret` is the body of main - // and `expected_ret` is a new parameter added to the public inputs. - if main.return_type != ast::Type::Unit { - let id = self.next_local_id(); - - let main = self.finished_functions.get_mut(&new_main_id).unwrap(); - main.parameters.push((id, false, "return".into(), main.return_type.clone())); - main.return_type = ast::Type::Unit; - - let name = "_".into(); - let typ = Self::convert_type(main_meta.return_type()); - let lhs = Box::new(ast::Expression::Ident(ast::Ident { - definition: Definition::Local(id), - mutable: false, - location: None, - name, - typ, - })); - - // Need to temporarily swap out main.body here because we cannot - // move out of it directly. - let tmp_body = ast::Expression::Literal(ast::Literal::Unit); - let main_body = std::mem::replace(&mut main.body, tmp_body); - - let rhs = Box::new(main_body); - let operator = ast::BinaryOp::Equal; - let eq = ast::Expression::Binary(ast::Binary { operator, lhs, rhs }); - - let location = self.interner.function_meta(&main_id).location; - main.body = ast::Expression::Constrain(Box::new(eq), location); - } - main_meta.into_abi(&self.interner) }