mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-30 23:33:27 +03:00
refactor execution of branch indicators and function returns
This commit is contained in:
parent
ced02462a0
commit
b94c70588e
@ -16,7 +16,13 @@
|
|||||||
|
|
||||||
//! Enforces an assert equals statement in a compiled Leo program.
|
//! Enforces an assert equals statement in a compiled Leo program.
|
||||||
|
|
||||||
use crate::{errors::ConsoleError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
use crate::{
|
||||||
|
errors::ConsoleError,
|
||||||
|
get_indicator_value,
|
||||||
|
program::ConstrainedProgram,
|
||||||
|
value::ConstrainedValue,
|
||||||
|
GroupType,
|
||||||
|
};
|
||||||
use leo_ast::{Expression, Span, Type};
|
use leo_ast::{Expression, Span, Type};
|
||||||
|
|
||||||
use snarkos_models::{
|
use snarkos_models::{
|
||||||
@ -30,7 +36,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
) -> Result<(), ConsoleError> {
|
) -> Result<(), ConsoleError> {
|
||||||
@ -42,12 +48,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
|
|
||||||
// If the indicator bit is false, do not evaluate the assertion
|
// If the indicator bit is false, do not evaluate the assertion
|
||||||
// This is okay since we are not enforcing any constraints
|
// This is okay since we are not enforcing any constraints
|
||||||
let false_boolean = Boolean::Constant(false);
|
if !get_indicator_value(indicator) {
|
||||||
|
return Ok(()); // Continue execution.
|
||||||
if let Some(indicator_bool) = indicator {
|
|
||||||
if indicator_bool.eq(&false_boolean) {
|
|
||||||
return Ok(()); // continue execution
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap assertion value and handle errors
|
// Unwrap assertion value and handle errors
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Evaluates a macro in a compiled Leo program.
|
//! Evaluates a macro in a compiled Leo program.
|
||||||
|
|
||||||
use crate::{errors::ConsoleError, program::ConstrainedProgram, GroupType};
|
use crate::{errors::ConsoleError, program::ConstrainedProgram, statement::get_indicator_value, GroupType};
|
||||||
use leo_ast::{ConsoleFunction, ConsoleFunctionCall};
|
use leo_ast::{ConsoleFunction, ConsoleFunctionCall};
|
||||||
|
|
||||||
use snarkos_models::{
|
use snarkos_models::{
|
||||||
@ -30,7 +30,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
console: ConsoleFunctionCall,
|
console: ConsoleFunctionCall,
|
||||||
) -> Result<(), ConsoleError> {
|
) -> Result<(), ConsoleError> {
|
||||||
match console.function {
|
match console.function {
|
||||||
@ -40,21 +40,21 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
ConsoleFunction::Debug(string) => {
|
ConsoleFunction::Debug(string) => {
|
||||||
let string = self.format(cs, file_scope, function_scope, string)?;
|
let string = self.format(cs, file_scope, function_scope, string)?;
|
||||||
|
|
||||||
if unwrap_indicator_value(indicator) {
|
if get_indicator_value(indicator) {
|
||||||
tracing::debug!("{}", string);
|
tracing::debug!("{}", string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConsoleFunction::Error(string) => {
|
ConsoleFunction::Error(string) => {
|
||||||
let string = self.format(cs, file_scope, function_scope, string)?;
|
let string = self.format(cs, file_scope, function_scope, string)?;
|
||||||
|
|
||||||
if unwrap_indicator_value(indicator) {
|
if get_indicator_value(indicator) {
|
||||||
tracing::error!("{}", string);
|
tracing::error!("{}", string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConsoleFunction::Log(string) => {
|
ConsoleFunction::Log(string) => {
|
||||||
let string = self.format(cs, file_scope, function_scope, string)?;
|
let string = self.format(cs, file_scope, function_scope, string)?;
|
||||||
|
|
||||||
if unwrap_indicator_value(indicator) {
|
if get_indicator_value(indicator) {
|
||||||
tracing::info!("{}", string);
|
tracing::info!("{}", string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,16 +63,3 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the indicator boolean gadget value or true if it is None
|
|
||||||
// This is okay since we are not enforcing any constraints
|
|
||||||
fn unwrap_indicator_value(indicator: Option<Boolean>) -> bool {
|
|
||||||
let false_boolean = Boolean::constant(false);
|
|
||||||
|
|
||||||
if let Some(indicator_bool) = indicator {
|
|
||||||
if indicator_bool.eq(&false_boolean) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
@ -135,6 +135,22 @@ impl StatementError {
|
|||||||
Self::new_from_span(message, span)
|
Self::new_from_span(message, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn multiple_returns(span: Span) -> Self {
|
||||||
|
let message =
|
||||||
|
format!("This function returns multiple times and produces unreachable circuits with undefined behavior.");
|
||||||
|
|
||||||
|
Self::new_from_span(message, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_returns(expected: Type, span: Span) -> Self {
|
||||||
|
let message = format!(
|
||||||
|
"function expected `{}` return type but no valid branches returned a result",
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
|
||||||
|
Self::new_from_span(message, span)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn select_fail(first: String, second: String, span: Span) -> Self {
|
pub fn select_fail(first: String, second: String, span: Span) -> Self {
|
||||||
let message = format!(
|
let message = format!(
|
||||||
"Conditional select gadget failed to select between `{}` or `{}`",
|
"Conditional select gadget failed to select between `{}` or `{}`",
|
||||||
|
@ -23,11 +23,11 @@ use crate::{
|
|||||||
GroupType,
|
GroupType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use leo_ast::{Expression, Function, FunctionInput, Span, Type};
|
use leo_ast::{Expression, Function, FunctionInput, Span};
|
||||||
|
|
||||||
use snarkos_models::{
|
use snarkos_models::{
|
||||||
curves::{Field, PrimeField},
|
curves::{Field, PrimeField},
|
||||||
gadgets::r1cs::ConstraintSystem,
|
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn check_arguments_length(expected: usize, actual: usize, span: &Span) -> Result<(), FunctionError> {
|
pub fn check_arguments_length(expected: usize, actual: usize, span: &Span) -> Result<(), FunctionError> {
|
||||||
@ -89,13 +89,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
|
|
||||||
// Evaluate every statement in the function and save all potential results
|
// Evaluate every statement in the function and save all potential results
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
|
let indicator = Boolean::constant(true);
|
||||||
|
|
||||||
for statement in function.statements.iter() {
|
for statement in function.statements.iter() {
|
||||||
let mut result = self.enforce_statement(
|
let mut result = self.enforce_statement(
|
||||||
cs,
|
cs,
|
||||||
scope,
|
scope,
|
||||||
&function_name,
|
&function_name,
|
||||||
None,
|
&indicator,
|
||||||
statement.clone(),
|
statement.clone(),
|
||||||
function.output.clone(),
|
function.output.clone(),
|
||||||
declared_circuit_reference,
|
declared_circuit_reference,
|
||||||
@ -105,26 +106,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Conditionally select a result based on returned indicators
|
// Conditionally select a result based on returned indicators
|
||||||
let mut return_values = ConstrainedValue::Tuple(vec![]);
|
Self::conditionally_select_result(cs, function.output, results, &function.span)
|
||||||
|
.map_err(|err| FunctionError::StatementError(err))
|
||||||
Self::conditionally_select_result(cs, &mut return_values, results, &function.span)?;
|
|
||||||
|
|
||||||
if let ConstrainedValue::Tuple(ref returns) = return_values {
|
|
||||||
let return_types = match function.output {
|
|
||||||
Some(Type::Tuple(types)) => types.len(),
|
|
||||||
Some(_) => 1usize,
|
|
||||||
None => 0usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
if return_types != returns.len() {
|
|
||||||
return Err(FunctionError::return_arguments_length(
|
|
||||||
return_types,
|
|
||||||
returns.len(),
|
|
||||||
function.span.clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(return_values)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,15 @@
|
|||||||
|
|
||||||
//! Enforces that one return value is produced in a compiled Leo program.
|
//! Enforces that one return value is produced in a compiled Leo program.
|
||||||
|
|
||||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
use crate::{
|
||||||
|
errors::StatementError,
|
||||||
|
get_indicator_value,
|
||||||
|
program::ConstrainedProgram,
|
||||||
|
value::ConstrainedValue,
|
||||||
|
GroupType,
|
||||||
|
};
|
||||||
|
|
||||||
use leo_ast::Span;
|
use leo_ast::{Span, Type};
|
||||||
|
|
||||||
use snarkos_models::{
|
use snarkos_models::{
|
||||||
curves::{Field, PrimeField},
|
curves::{Field, PrimeField},
|
||||||
@ -29,49 +35,84 @@ use snarkos_models::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
/// iterates through a vector of results and selects one based off of indicators
|
///
|
||||||
|
/// Returns a conditionally selected result from the given possible function returns and
|
||||||
|
/// given function return type.
|
||||||
|
///
|
||||||
pub fn conditionally_select_result<CS: ConstraintSystem<F>>(
|
pub fn conditionally_select_result<CS: ConstraintSystem<F>>(
|
||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
return_value: &mut ConstrainedValue<F, G>,
|
expected_return: Option<Type>,
|
||||||
results: Vec<(Option<Boolean>, ConstrainedValue<F, G>)>,
|
results: Vec<(Boolean, ConstrainedValue<F, G>)>,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
) -> Result<(), StatementError> {
|
) -> Result<ConstrainedValue<F, G>, StatementError> {
|
||||||
// if there are no results, continue
|
// Initialize empty return value.
|
||||||
if results.is_empty() {
|
let mut return_value = ConstrainedValue::Tuple(vec![]);
|
||||||
return Ok(());
|
|
||||||
|
// If the function does not expect a return type, then make sure there are no returned results.
|
||||||
|
let return_type = match expected_return {
|
||||||
|
Some(return_type) => return_type,
|
||||||
|
None => {
|
||||||
|
if results.is_empty() {
|
||||||
|
// If the function has no returns, then return an empty tuple.
|
||||||
|
return Ok(return_value);
|
||||||
|
} else {
|
||||||
|
return Err(StatementError::invalid_number_of_returns(
|
||||||
|
0,
|
||||||
|
results.len(),
|
||||||
|
span.to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Error if the function or one of its branches does not return.
|
||||||
|
if let None = results.iter().find(|(indicator, _res)| get_indicator_value(indicator)) {
|
||||||
|
return Err(StatementError::no_returns(return_type, span.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If all indicators are none, then there are no branch conditions in the function.
|
// Find the return value
|
||||||
// We simply return the last result.
|
let mut ignored = vec![];
|
||||||
|
let mut found_return = false;
|
||||||
if results.iter().all(|(indicator, _res)| indicator.is_none()) {
|
for (indicator, result) in results.into_iter() {
|
||||||
let result = &results[results.len() - 1].1;
|
// Error if a statement returned a result with an incorrect type
|
||||||
|
let result_type = result.to_type(span)?;
|
||||||
*return_value = result.clone();
|
if return_type != result_type {
|
||||||
|
return Err(StatementError::arguments_type(
|
||||||
return Ok(());
|
&return_type,
|
||||||
}
|
&result_type,
|
||||||
|
span.to_owned(),
|
||||||
// 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));
|
if get_indicator_value(&indicator) {
|
||||||
let selected_value = ConstrainedValue::conditionally_select(
|
// Error if we already have a return value.
|
||||||
cs.ns(|| format!("select {} {}:{}", result, span.line, span.start)),
|
if found_return {
|
||||||
&condition,
|
return Err(StatementError::multiple_returns(span.to_owned()));
|
||||||
&result,
|
} else {
|
||||||
return_value,
|
// Set the function return value.
|
||||||
)
|
return_value = result;
|
||||||
.map_err(|_| StatementError::select_fail(result.to_string(), return_value.to_string(), span.to_owned()))?;
|
found_return = true;
|
||||||
|
}
|
||||||
*return_value = selected_value;
|
} else {
|
||||||
|
// Ignore a possible function return value.
|
||||||
|
ignored.push((indicator, result))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
// Conditionally select out the ignored results in the circuit.
|
||||||
|
//
|
||||||
|
// 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 ignored.into_iter().enumerate() {
|
||||||
|
return_value = ConstrainedValue::conditionally_select(
|
||||||
|
cs.ns(|| format!("select result {} {}:{}", i, span.line, span.start)),
|
||||||
|
&indicator,
|
||||||
|
&result,
|
||||||
|
&return_value,
|
||||||
|
)
|
||||||
|
.map_err(|_| StatementError::select_fail(result.to_string(), return_value.to_string(), span.to_owned()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(return_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,14 +34,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
name: &str,
|
name: &str,
|
||||||
range_or_expression: RangeOrExpression,
|
range_or_expression: RangeOrExpression,
|
||||||
mut new_value: ConstrainedValue<F, G>,
|
mut new_value: ConstrainedValue<F, G>,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
) -> Result<(), StatementError> {
|
) -> Result<(), StatementError> {
|
||||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
|
||||||
|
|
||||||
// Resolve index so we know if we are assigning to a single value or a range of values
|
// Resolve index so we know if we are assigning to a single value or a range of values
|
||||||
match range_or_expression {
|
match range_or_expression {
|
||||||
RangeOrExpression::Expression(index) => {
|
RangeOrExpression::Expression(index) => {
|
||||||
@ -54,7 +52,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
|
|
||||||
let selected_value = ConstrainedValue::conditionally_select(
|
let selected_value = ConstrainedValue::conditionally_select(
|
||||||
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
||||||
&condition,
|
indicator,
|
||||||
&new_value,
|
&new_value,
|
||||||
&old[index],
|
&old[index],
|
||||||
)
|
)
|
||||||
@ -90,7 +88,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
};
|
};
|
||||||
let selected_array = ConstrainedValue::conditionally_select(
|
let selected_array = ConstrainedValue::conditionally_select(
|
||||||
cs.ns(|| format!("select {} {}:{}", new_array, span.line, span.start)),
|
cs.ns(|| format!("select {} {}:{}", new_array, span.line, span.start)),
|
||||||
&condition,
|
indicator,
|
||||||
&new_array,
|
&new_array,
|
||||||
old_array,
|
old_array,
|
||||||
)
|
)
|
||||||
|
@ -42,7 +42,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
declared_circuit_reference: &str,
|
declared_circuit_reference: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
assignee: Assignee,
|
assignee: Assignee,
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
@ -55,14 +55,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
|
|
||||||
// Mutate the old value into the new value
|
// Mutate the old value into the new value
|
||||||
if assignee.accesses.is_empty() {
|
if assignee.accesses.is_empty() {
|
||||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
|
||||||
let old_value = self.get_mutable_assignee(&variable_name, span)?;
|
let old_value = self.get_mutable_assignee(&variable_name, span)?;
|
||||||
|
|
||||||
new_value.resolve_type(Some(old_value.to_type(&span)?), span)?;
|
new_value.resolve_type(Some(old_value.to_type(&span)?), span)?;
|
||||||
|
|
||||||
let selected_value = ConstrainedValue::conditionally_select(
|
let selected_value = ConstrainedValue::conditionally_select(
|
||||||
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
||||||
&condition,
|
indicator,
|
||||||
&new_value,
|
&new_value,
|
||||||
old_value,
|
old_value,
|
||||||
)
|
)
|
||||||
|
@ -31,14 +31,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
pub fn mutate_circuit_variable<CS: ConstraintSystem<F>>(
|
pub fn mutate_circuit_variable<CS: ConstraintSystem<F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
circuit_name: &str,
|
circuit_name: &str,
|
||||||
variable_name: Identifier,
|
variable_name: Identifier,
|
||||||
mut new_value: ConstrainedValue<F, G>,
|
mut new_value: ConstrainedValue<F, G>,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
) -> Result<ConstrainedValue<F, G>, StatementError> {
|
) -> Result<ConstrainedValue<F, G>, StatementError> {
|
||||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
|
||||||
|
|
||||||
// Get the mutable circuit by name
|
// Get the mutable circuit by name
|
||||||
match self.get_mutable_assignee(circuit_name, span)? {
|
match self.get_mutable_assignee(circuit_name, span)? {
|
||||||
ConstrainedValue::CircuitExpression(_variable, members) => {
|
ConstrainedValue::CircuitExpression(_variable, members) => {
|
||||||
@ -70,7 +68,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
// Conditionally select the value if this branch is executed.
|
// Conditionally select the value if this branch is executed.
|
||||||
let mut selected_value = ConstrainedValue::conditionally_select(
|
let mut selected_value = ConstrainedValue::conditionally_select(
|
||||||
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
||||||
&condition,
|
indicator,
|
||||||
&new_value,
|
&new_value,
|
||||||
&member.1,
|
&member.1,
|
||||||
)
|
)
|
||||||
|
@ -31,15 +31,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
pub fn assign_tuple<CS: ConstraintSystem<F>>(
|
pub fn assign_tuple<CS: ConstraintSystem<F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
name: &str,
|
name: &str,
|
||||||
index: PositiveNumber,
|
index: PositiveNumber,
|
||||||
mut new_value: ConstrainedValue<F, G>,
|
mut new_value: ConstrainedValue<F, G>,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
) -> Result<(), StatementError> {
|
) -> Result<(), StatementError> {
|
||||||
// Get the indicator value.
|
|
||||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
|
||||||
|
|
||||||
// Parse the index.
|
// Parse the index.
|
||||||
let index_usize = parse_index(&index, &span)?;
|
let index_usize = parse_index(&index, &span)?;
|
||||||
|
|
||||||
@ -50,7 +47,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
|
|
||||||
let selected_value = ConstrainedValue::conditionally_select(
|
let selected_value = ConstrainedValue::conditionally_select(
|
||||||
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
||||||
&condition,
|
indicator,
|
||||||
&new_value,
|
&new_value,
|
||||||
&old[index_usize],
|
&old[index_usize],
|
||||||
)
|
)
|
||||||
|
@ -30,7 +30,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
statements: Vec<Statement>,
|
statements: Vec<Statement>,
|
||||||
return_type: Option<Type>,
|
return_type: Option<Type>,
|
||||||
) -> StatementResult<Vec<IndicatorAndConstrainedValue<F, G>>> {
|
) -> StatementResult<Vec<IndicatorAndConstrainedValue<F, G>>> {
|
||||||
|
@ -49,15 +49,15 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
statement: ConditionalStatement,
|
statement: ConditionalStatement,
|
||||||
return_type: Option<Type>,
|
return_type: Option<Type>,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
) -> StatementResult<Vec<IndicatorAndConstrainedValue<F, G>>> {
|
) -> StatementResult<Vec<IndicatorAndConstrainedValue<F, G>>> {
|
||||||
let statement_string = statement.to_string();
|
let statement_string = statement.to_string();
|
||||||
|
|
||||||
// Inherit the indicator from a previous conditional statement or assume that we are the outer parent
|
// Inherit an indicator from a previous statement.
|
||||||
let outer_indicator = indicator.unwrap_or(Boolean::Constant(true));
|
let outer_indicator = indicator;
|
||||||
|
|
||||||
// Evaluate the conditional boolean as the inner indicator
|
// Evaluate the conditional boolean as the inner indicator
|
||||||
let inner_indicator = match self.enforce_expression(
|
let inner_indicator = match self.enforce_expression(
|
||||||
@ -72,7 +72,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If outer_indicator && inner_indicator, then select branch 1
|
// If outer_indicator && inner_indicator, then select branch 1
|
||||||
let outer_indicator_string = indicator_to_string(&outer_indicator);
|
let outer_indicator_string = indicator_to_string(outer_indicator);
|
||||||
let inner_indicator_string = indicator_to_string(&inner_indicator);
|
let inner_indicator_string = indicator_to_string(&inner_indicator);
|
||||||
let branch_1_name = format!(
|
let branch_1_name = format!(
|
||||||
"branch indicator 1 {} && {}",
|
"branch indicator 1 {} && {}",
|
||||||
@ -80,7 +80,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
);
|
);
|
||||||
let branch_1_indicator = Boolean::and(
|
let branch_1_indicator = Boolean::and(
|
||||||
&mut cs.ns(|| format!("branch 1 {} {}:{}", statement_string, span.line, span.start)),
|
&mut cs.ns(|| format!("branch 1 {} {}:{}", statement_string, span.line, span.start)),
|
||||||
&outer_indicator,
|
outer_indicator,
|
||||||
&inner_indicator,
|
&inner_indicator,
|
||||||
)
|
)
|
||||||
.map_err(|_| StatementError::indicator_calculation(branch_1_name, span.to_owned()))?;
|
.map_err(|_| StatementError::indicator_calculation(branch_1_name, span.to_owned()))?;
|
||||||
@ -92,7 +92,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs,
|
cs,
|
||||||
file_scope,
|
file_scope,
|
||||||
function_scope,
|
function_scope,
|
||||||
Some(branch_1_indicator),
|
&branch_1_indicator,
|
||||||
statement.statements,
|
statement.statements,
|
||||||
return_type.clone(),
|
return_type.clone(),
|
||||||
)?;
|
)?;
|
||||||
@ -120,7 +120,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs,
|
cs,
|
||||||
file_scope,
|
file_scope,
|
||||||
function_scope,
|
function_scope,
|
||||||
Some(branch_2_indicator),
|
&branch_2_indicator,
|
||||||
*nested,
|
*nested,
|
||||||
return_type,
|
return_type,
|
||||||
span,
|
span,
|
||||||
@ -129,7 +129,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs,
|
cs,
|
||||||
file_scope,
|
file_scope,
|
||||||
function_scope,
|
function_scope,
|
||||||
Some(branch_2_indicator),
|
&branch_2_indicator,
|
||||||
statements,
|
statements,
|
||||||
return_type,
|
return_type,
|
||||||
)?,
|
)?,
|
||||||
|
@ -42,7 +42,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
index: Identifier,
|
index: Identifier,
|
||||||
start: Expression,
|
start: Expression,
|
||||||
stop: Expression,
|
stop: Expression,
|
||||||
|
@ -25,7 +25,7 @@ use snarkos_models::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub type StatementResult<T> = Result<T, StatementError>;
|
pub type StatementResult<T> = Result<T, StatementError>;
|
||||||
pub type IndicatorAndConstrainedValue<T, U> = (Option<Boolean>, ConstrainedValue<T, U>);
|
pub type IndicatorAndConstrainedValue<T, U> = (Boolean, ConstrainedValue<T, U>);
|
||||||
|
|
||||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
///
|
///
|
||||||
@ -41,7 +41,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
file_scope: &str,
|
file_scope: &str,
|
||||||
function_scope: &str,
|
function_scope: &str,
|
||||||
indicator: Option<Boolean>,
|
indicator: &Boolean,
|
||||||
statement: Statement,
|
statement: Statement,
|
||||||
return_type: Option<Type>,
|
return_type: Option<Type>,
|
||||||
declared_circuit_reference: &str,
|
declared_circuit_reference: &str,
|
||||||
@ -51,7 +51,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
match statement {
|
match statement {
|
||||||
Statement::Return(expression, span) => {
|
Statement::Return(expression, span) => {
|
||||||
let return_value = (
|
let return_value = (
|
||||||
indicator,
|
indicator.to_owned(),
|
||||||
self.enforce_return_statement(cs, file_scope, function_scope, expression, return_type, &span)?,
|
self.enforce_return_statement(cs, file_scope, function_scope, expression, return_type, &span)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
_ => return Err(StatementError::unassigned(expression_string, span)),
|
_ => return Err(StatementError::unassigned(expression_string, span)),
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = (indicator, value);
|
let result = (indicator.to_owned(), value);
|
||||||
|
|
||||||
results.push(result);
|
results.push(result);
|
||||||
}
|
}
|
||||||
@ -135,3 +135,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the indicator boolean gadget value.
|
||||||
|
/// We can directly compare a boolean constant to the indicator since we are not enforcing any
|
||||||
|
/// constraints
|
||||||
|
pub fn get_indicator_value(indicator: &Boolean) -> bool {
|
||||||
|
indicator.eq(&Boolean::constant(true))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user