diff --git a/Cargo.lock b/Cargo.lock index 85e1eab4e0..3fccd5be74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,30 +86,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.53.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" -dependencies = [ - "bitflags", - "cexpr", - "cfg-if", - "clang-sys", - "clap", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2 1.0.18", - "quote 1.0.7", - "regex", - "rustc-hash", - "shlex", - "which", -] - [[package]] name = "bitflags" version = "1.2.1" @@ -173,24 +149,6 @@ version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -[[package]] -name = "cc" -version = "1.0.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cexpr" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "0.1.10" @@ -206,17 +164,6 @@ dependencies = [ "envmnt", ] -[[package]] -name = "clang-sys" -version = "0.29.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "2.33.1" @@ -301,36 +248,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "curl" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "762e34611d2d5233a506a79072be944fddd057db2f18e04c0d6fa79e3fd466fd" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2", - "winapi", -] - -[[package]] -name = "curl-sys" -version = "0.4.31+curl-7.70.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcd62757cc4f5ab9404bc6ca9f0ae447e729a1403948ce5106bd588ceac6a3b0" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "winapi", -] - [[package]] name = "derivative" version = "2.1.1" @@ -484,12 +401,6 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - [[package]] name = "hermit-abi" version = "0.1.14" @@ -538,15 +449,6 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" -[[package]] -name = "jobserver" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" -dependencies = [ - "libc", -] - [[package]] name = "jsonrpc-core" version = "14.2.0" @@ -566,12 +468,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" - [[package]] name = "leo" version = "0.1.0" @@ -665,40 +561,6 @@ version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" -[[package]] -name = "libloading" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" -dependencies = [ - "cc", - "winapi", -] - -[[package]] -name = "librocksdb-sys" -version = "6.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883213ae3d09bfc3d104aefe94b25ebb183b6f4d3a515b23b14817e1f4854005" -dependencies = [ - "bindgen", - "cc", - "glob", - "libc", -] - -[[package]] -name = "libz-sys" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "log" version = "0.4.8" @@ -750,16 +612,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0" -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "memchr", - "version_check", -] - [[package]] name = "num_cpus" version = "1.13.0" @@ -782,31 +634,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -[[package]] -name = "openssl-probe" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" - -[[package]] -name = "openssl-sys" -version = "0.9.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pest" version = "2.1.3" @@ -863,12 +690,6 @@ dependencies = [ "sha-1", ] -[[package]] -name = "pkg-config" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" - [[package]] name = "ppv-lite86" version = "0.2.8" @@ -992,12 +813,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" - [[package]] name = "regex" version = "1.3.9" @@ -1016,28 +831,12 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" -[[package]] -name = "rocksdb" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12069b106981c6103d3eab7dd1c86751482d0779a520b7c14954c8b586c1e643" -dependencies = [ - "libc", - "librocksdb-sys", -] - [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rusty-hook" version = "0.11.1" @@ -1056,16 +855,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - [[package]] name = "scopeguard" version = "1.1.0" @@ -1139,12 +928,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "shlex" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" - [[package]] name = "single" version = "1.0.0" @@ -1207,10 +990,8 @@ dependencies = [ "base58", "bech32", "bincode", - "curl", "hex", "jsonrpc-core", - "rocksdb", "thiserror", ] @@ -1255,18 +1036,6 @@ dependencies = [ "snarkos-errors", ] -[[package]] -name = "socket2" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", -] - [[package]] name = "strsim" version = "0.8.0" @@ -1399,12 +1168,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -[[package]] -name = "vcpkg" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" - [[package]] name = "vec_map" version = "0.8.2" @@ -1429,15 +1192,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "which" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" -dependencies = [ - "libc", -] - [[package]] name = "winapi" version = "0.3.8" diff --git a/compiler/src/constraints/function.rs b/compiler/src/constraints/function.rs index 3603d7a4a3..c7aa8b0b48 100644 --- a/compiler/src/constraints/function.rs +++ b/compiler/src/constraints/function.rs @@ -11,9 +11,13 @@ use crate::{ }; use leo_types::{Expression, Function, InputValue, Integer, Program, Span, Type}; +use crate::errors::StatementError; use snarkos_models::{ curves::{Field, PrimeField}, - gadgets::r1cs::ConstraintSystem, + gadgets::{ + r1cs::ConstraintSystem, + utilities::{boolean::Boolean, select::CondSelectGadget}, + }, }; impl> ConstrainedProgram { @@ -45,6 +49,51 @@ impl> ConstrainedProgram { } } + /// iterates through a vector of results and selects one based off of indicators + fn conditionally_select_result>( + cs: &mut CS, + return_value: &mut ConstrainedValue, + results: Vec<(Option, ConstrainedValue)>, + span: Span, + ) -> Result<(), StatementError> { + // if there are no results, continue + if results.len() == 0 { + return Ok(()); + } + + // If all indicators are none, then there are no branch conditions in the function. + // We simply return the last result. + + if let None = results.iter().find(|(indicator, _res)| indicator.is_some()) { + let result = &results[results.len() - 1].1; + + *return_value = result.clone(); + + return Ok(()); + } + + // If there are branches in the function we need to use the `ConditionalSelectGadget` to parse through and select the correct one. + // This can be thought of as de-multiplexing all previous wires that may have returned results into one. + for (i, (indicator, result)) in results.into_iter().enumerate() { + // Set the first value as the starting point + if i == 0 { + *return_value = result.clone(); + } + + let condition = indicator.unwrap_or(Boolean::Constant(true)); + let name_unique = format!("select {} {}:{}", result, span.line, span.start); + let selected_value = + ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &result, return_value) + .map_err(|_| { + StatementError::select_fail(result.to_string(), return_value.to_string(), span.clone()) + })?; + + *return_value = selected_value; + } + + Ok(()) + } + pub(crate) fn enforce_function>( &mut self, cs: &mut CS, @@ -79,26 +128,36 @@ impl> ConstrainedProgram { self.store(input_program_identifier, input_value); } - // Evaluate function statements + // Evaluate every statement in the function and save all potential results - let mut return_values = ConstrainedValue::Return(vec![]); + let mut results = vec![]; for statement in function.statements.iter() { - if let Some(returned) = self.enforce_statement( + let mut result = self.enforce_statement( cs, scope.clone(), function_name.clone(), None, statement.clone(), function.returns.clone(), - )? { - return_values = returned; - break; - } + )?; + + results.append(&mut result); } + // Conditionally select a result based on returned indicators + let mut return_values = ConstrainedValue::Return(vec![]); + + Self::conditionally_select_result(cs, &mut return_values, results, function.span.clone())?; + if let ConstrainedValue::Return(ref returns) = return_values { - Self::check_arguments_length(function.returns.len(), returns.len(), function.span.clone())?; + if function.returns.len() != returns.len() { + return Err(FunctionError::return_arguments_length( + function.returns.len(), + returns.len(), + function.span.clone(), + )); + } } Ok(return_values) diff --git a/compiler/src/constraints/statement.rs b/compiler/src/constraints/statement.rs index 27e442dcb5..22ad5a9211 100644 --- a/compiler/src/constraints/statement.rs +++ b/compiler/src/constraints/statement.rs @@ -390,24 +390,23 @@ impl> ConstrainedProgram { indicator: Option, statements: Vec, return_types: Vec, - ) -> Result>, StatementError> { - let mut res = None; - // Evaluate statements and possibly return early + ) -> Result, ConstrainedValue)>, StatementError> { + let mut results = vec![]; + // Evaluate statements. Only allow a single return argument to be returned. for statement in statements.iter() { - if let Some(early_return) = self.enforce_statement( + let mut value = self.enforce_statement( cs, file_scope.clone(), function_scope.clone(), indicator.clone(), statement.clone(), return_types.clone(), - )? { - res = Some(early_return); - break; - } + )?; + + results.append(&mut value); } - Ok(res) + Ok(results) } /// Enforces a statements.conditional statement with one or more branches. @@ -423,7 +422,7 @@ impl> ConstrainedProgram { statement: ConditionalStatement, return_types: Vec, span: Span, - ) -> Result>, StatementError> { + ) -> Result, ConstrainedValue)>, StatementError> { let statement_string = statement.to_string(); let outer_indicator = indicator.unwrap_or(Boolean::Constant(true)); @@ -459,8 +458,10 @@ impl> ConstrainedProgram { ) .map_err(|_| StatementError::indicator_calculation(branch_1_name, span.clone()))?; + let mut results = vec![]; + // Execute branch 1 - self.evaluate_branch( + let mut branch_1_result = self.evaluate_branch( cs, file_scope.clone(), function_scope.clone(), @@ -469,6 +470,8 @@ impl> ConstrainedProgram { return_types.clone(), )?; + results.append(&mut branch_1_result); + // Determine nested branch 2 selection let inner_indicator = inner_indicator.not(); let inner_indicator_string = inner_indicator @@ -487,7 +490,7 @@ impl> ConstrainedProgram { .map_err(|_| StatementError::indicator_calculation(branch_2_name, span.clone()))?; // Execute branch 2 - match statement.next { + let mut branch_2_result = match statement.next { Some(next) => match next { ConditionalNestedOrEndStatement::Nested(nested) => self.enforce_conditional_statement( cs, @@ -497,7 +500,7 @@ impl> ConstrainedProgram { *nested, return_types, span, - ), + )?, ConditionalNestedOrEndStatement::End(statements) => self.evaluate_branch( cs, file_scope, @@ -505,10 +508,14 @@ impl> ConstrainedProgram { Some(branch_2_indicator), statements, return_types, - ), + )?, }, - None => Ok(None), - } + None => vec![], + }; + + results.append(&mut branch_2_result); + + Ok(results) } fn enforce_for_statement>( @@ -523,8 +530,8 @@ impl> ConstrainedProgram { statements: Vec, return_types: Vec, span: Span, - ) -> Result>, StatementError> { - let mut res = None; + ) -> Result, ConstrainedValue)>, StatementError> { + let mut results = vec![]; let from = start.to_usize(span.clone())?; let to = stop.to_usize(span.clone())?; @@ -540,20 +547,19 @@ impl> ConstrainedProgram { // Evaluate statements and possibly return early let name_unique = format!("for loop iteration {} {}:{}", i, span.line, span.start); - if let Some(early_return) = self.evaluate_branch( + let mut result = self.evaluate_branch( &mut cs.ns(|| name_unique), file_scope.clone(), function_scope.clone(), indicator, statements.clone(), return_types.clone(), - )? { - res = Some(early_return); - break; - } + )?; + + results.append(&mut result); } - Ok(res) + Ok(results) } fn enforce_assert_eq_statement>( @@ -571,6 +577,10 @@ impl> ConstrainedProgram { Ok(result.map_err(|_| StatementError::assertion_failed(left.to_string(), right.to_string(), span))?) } + /// Enforce a program statement. + /// Returns a Vector of (indicator, value) tuples. + /// Each evaluated statement may execute of one or more statements that may return early. + /// To indicate which of these return values to take we conditionally select that value with the indicator bit. pub(crate) fn enforce_statement>( &mut self, cs: &mut CS, @@ -579,18 +589,16 @@ impl> ConstrainedProgram { indicator: Option, statement: Statement, return_types: Vec, - ) -> Result>, StatementError> { - let mut res = None; + ) -> Result, ConstrainedValue)>, StatementError> { + let mut results = vec![]; match statement { Statement::Return(expressions, span) => { - res = Some(self.enforce_return_statement( - cs, - file_scope, - function_scope, - expressions, - return_types, - span, - )?); + let return_value = ( + indicator, + self.enforce_return_statement(cs, file_scope, function_scope, expressions, return_types, span)?, + ); + + results.push(return_value); } Statement::Definition(declare, variable, expression, span) => { self.enforce_definition_statement(cs, file_scope, function_scope, declare, variable, expression, span)?; @@ -602,7 +610,7 @@ impl> ConstrainedProgram { self.enforce_multiple_definition_statement(cs, file_scope, function_scope, variables, function, span)?; } Statement::Conditional(statement, span) => { - if let Some(early_return) = self.enforce_conditional_statement( + let mut result = self.enforce_conditional_statement( cs, file_scope, function_scope, @@ -610,12 +618,12 @@ impl> ConstrainedProgram { statement, return_types, span, - )? { - res = Some(early_return) - } + )?; + + results.append(&mut result); } Statement::For(index, start, stop, statements, span) => { - if let Some(early_return) = self.enforce_for_statement( + let mut result = self.enforce_for_statement( cs, file_scope, function_scope, @@ -626,9 +634,9 @@ impl> ConstrainedProgram { statements, return_types, span, - )? { - res = Some(early_return) - } + )?; + + results.append(&mut result); } Statement::AssertEq(left, right, span) => { let (resolved_left, resolved_right) = @@ -637,17 +645,25 @@ impl> ConstrainedProgram { self.enforce_assert_eq_statement(cs, indicator, &resolved_left, &resolved_right, span)?; } Statement::Expression(expression, span) => { - match self.enforce_expression(cs, file_scope, function_scope, &vec![], expression.clone())? { + let expression_string = expression.to_string(); + let value = self.enforce_expression(cs, file_scope, function_scope, &vec![], expression)?; + + // handle empty return value cases + match &value { ConstrainedValue::Return(values) => { if !values.is_empty() { - return Err(StatementError::unassigned(expression.to_string(), span)); + return Err(StatementError::unassigned(expression_string, span)); } } - _ => return Err(StatementError::unassigned(expression.to_string(), span)), + _ => return Err(StatementError::unassigned(expression_string, span)), } + + let result = (indicator, value); + + results.push(result); } }; - Ok(res) + Ok(results) } } diff --git a/compiler/src/constraints/value.rs b/compiler/src/constraints/value.rs index 824cea54d5..81a0f3602a 100644 --- a/compiler/src/constraints/value.rs +++ b/compiler/src/constraints/value.rs @@ -319,16 +319,59 @@ impl> CondSelectGadget for Constrained } (ConstrainedValue::Array(arr_1), ConstrainedValue::Array(arr_2)) => { let mut array = vec![]; + for (i, (first, second)) in arr_1.into_iter().zip(arr_2.into_iter()).enumerate() { array.push(Self::conditionally_select( - cs.ns(|| format!("array[{}]", i,)), + cs.ns(|| format!("array[{}]", i)), cond, first, second, )?); } + ConstrainedValue::Array(array) } + (ConstrainedValue::Function(identifier_1, function_1), ConstrainedValue::Function(_, _)) => { + // This is a no-op. functions cannot hold circuit values + // However, we must return a result here + ConstrainedValue::Function(identifier_1.clone(), function_1.clone()) + } + ( + ConstrainedValue::CircuitExpression(identifier, members_1), + ConstrainedValue::CircuitExpression(_identifier, members_2), + ) => { + let mut members = vec![]; + + for (i, (first, second)) in members_1.into_iter().zip(members_2.into_iter()).enumerate() { + members.push(ConstrainedCircuitMember::conditionally_select( + cs.ns(|| format!("circuit member[{}]", i)), + cond, + first, + second, + )?); + } + + ConstrainedValue::CircuitExpression(identifier.clone(), members) + } + (ConstrainedValue::Return(returns_1), ConstrainedValue::Return(returns_2)) => { + let mut returns = vec![]; + + for (i, (first, second)) in returns_1.into_iter().zip(returns_2.into_iter()).enumerate() { + returns.push(Self::conditionally_select( + cs.ns(|| format!("return[{}]", i)), + cond, + first, + second, + )?); + } + + ConstrainedValue::Return(returns) + } + (ConstrainedValue::Static(first), ConstrainedValue::Static(second)) => { + let value = Self::conditionally_select(cs, cond, first, second)?; + + ConstrainedValue::Static(Box::new(value)) + } (ConstrainedValue::Mutable(first), _) => Self::conditionally_select(cs, cond, first, second)?, (_, ConstrainedValue::Mutable(second)) => Self::conditionally_select(cs, cond, first, second)?, (_, _) => return Err(SynthesisError::Unsatisfiable), @@ -339,3 +382,21 @@ impl> CondSelectGadget for Constrained unimplemented!() //lower bound 1, upper bound 128 or length of static array } } + +impl> CondSelectGadget for ConstrainedCircuitMember { + fn conditionally_select>( + cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + // identifiers will be the same + let value = ConstrainedValue::conditionally_select(cs, cond, &first.1, &second.1)?; + + Ok(ConstrainedCircuitMember(first.0.clone(), value)) + } + + fn cost() -> usize { + unimplemented!() + } +} diff --git a/compiler/src/errors/constraints/function.rs b/compiler/src/errors/constraints/function.rs index 6c7a269bf2..5ece62a10e 100644 --- a/compiler/src/errors/constraints/function.rs +++ b/compiler/src/errors/constraints/function.rs @@ -58,4 +58,10 @@ impl FunctionError { Self::new_from_span(message, span) } + + pub fn return_arguments_length(expected: usize, actual: usize, span: Span) -> Self { + let message = format!("function expected {} returns, found {} returns", expected, actual); + + Self::new_from_span(message, span) + } } diff --git a/compiler/tests/statements/conditional/mod.rs b/compiler/tests/statements/conditional/mod.rs index 65f5ba15c6..34e3cf0402 100644 --- a/compiler/tests/statements/conditional/mod.rs +++ b/compiler/tests/statements/conditional/mod.rs @@ -127,3 +127,20 @@ fn test_nested() { program_false_false_0.set_inputs(vec![Some(InputValue::Boolean(false)), Some(InputValue::Boolean(false))]); output_number(program_false_false_0, 0u32); } + +#[test] +fn test_multiple_returns() { + let bytes = include_bytes!("multiple_returns.leo"); + let mut program_true_1 = parse_program(bytes).unwrap(); + let mut program_false_0 = program_true_1.clone(); + + // Check that an input value of true returns 1 and satisfies the constraint system + + program_true_1.set_inputs(vec![Some(InputValue::Boolean(true))]); + output_number(program_true_1, 1u32); + + // Check that an input value of false returns 0 and satisfies the constraint system + + program_false_0.set_inputs(vec![Some(InputValue::Boolean(false))]); + output_number(program_false_0, 0u32); +} diff --git a/compiler/tests/statements/conditional/multiple_returns.leo b/compiler/tests/statements/conditional/multiple_returns.leo new file mode 100644 index 0000000000..434bd8e458 --- /dev/null +++ b/compiler/tests/statements/conditional/multiple_returns.leo @@ -0,0 +1,7 @@ +function main(cond: bool) -> u32 { + if cond { + return 1u32 + } else { + return 0u32 + } +} \ No newline at end of file