mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-25 11:12:48 +03:00
Merge pull request #72 from AleoHQ/feature/multiple-returns
Feature/multiple returns
This commit is contained in:
commit
b84f60b39f
246
Cargo.lock
generated
246
Cargo.lock
generated
@ -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"
|
||||
|
@ -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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
@ -45,6 +49,51 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
}
|
||||
}
|
||||
|
||||
/// iterates through a vector of results and selects one based off of indicators
|
||||
fn conditionally_select_result<CS: ConstraintSystem<F>>(
|
||||
cs: &mut CS,
|
||||
return_value: &mut ConstrainedValue<F, G>,
|
||||
results: Vec<(Option<Boolean>, ConstrainedValue<F, G>)>,
|
||||
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<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
@ -79,26 +128,36 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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)
|
||||
|
@ -390,24 +390,23 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
indicator: Option<Boolean>,
|
||||
statements: Vec<Statement>,
|
||||
return_types: Vec<Type>,
|
||||
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
|
||||
let mut res = None;
|
||||
// Evaluate statements and possibly return early
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, 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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
statement: ConditionalStatement,
|
||||
return_types: Vec<Type>,
|
||||
span: Span,
|
||||
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
||||
let statement_string = statement.to_string();
|
||||
let outer_indicator = indicator.unwrap_or(Boolean::Constant(true));
|
||||
|
||||
@ -459,8 +458,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
)
|
||||
.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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
.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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
*nested,
|
||||
return_types,
|
||||
span,
|
||||
),
|
||||
)?,
|
||||
ConditionalNestedOrEndStatement::End(statements) => self.evaluate_branch(
|
||||
cs,
|
||||
file_scope,
|
||||
@ -505,10 +508,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
Some(branch_2_indicator),
|
||||
statements,
|
||||
return_types,
|
||||
),
|
||||
)?,
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
results.append(&mut branch_2_result);
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn enforce_for_statement<CS: ConstraintSystem<F>>(
|
||||
@ -523,8 +530,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
statements: Vec<Statement>,
|
||||
return_types: Vec<Type>,
|
||||
span: Span,
|
||||
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
|
||||
let mut res = None;
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
||||
let mut results = vec![];
|
||||
let from = start.to_usize(span.clone())?;
|
||||
let to = stop.to_usize(span.clone())?;
|
||||
|
||||
@ -540,20 +547,19 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
|
||||
// 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<CS: ConstraintSystem<F>>(
|
||||
@ -571,6 +577,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
@ -579,18 +589,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
indicator: Option<Boolean>,
|
||||
statement: Statement,
|
||||
return_types: Vec<Type>,
|
||||
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
|
||||
let mut res = None;
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, 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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -319,16 +319,59 @@ impl<F: Field + PrimeField, G: GroupType<F>> CondSelectGadget<F> 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<F: Field + PrimeField, G: GroupType<F>> CondSelectGadget<F> for Constrained
|
||||
unimplemented!() //lower bound 1, upper bound 128 or length of static array
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> CondSelectGadget<F> for ConstrainedCircuitMember<F, G> {
|
||||
fn conditionally_select<CS: ConstraintSystem<F>>(
|
||||
cs: CS,
|
||||
cond: &Boolean,
|
||||
first: &Self,
|
||||
second: &Self,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
// 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!()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
function main(cond: bool) -> u32 {
|
||||
if cond {
|
||||
return 1u32
|
||||
} else {
|
||||
return 0u32
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user