support tuple access and comparison

This commit is contained in:
collin 2020-08-10 19:12:38 -07:00
parent e85d6499d0
commit 0e8473658e
3 changed files with 145 additions and 36 deletions

View File

@ -113,6 +113,12 @@ impl StatementError {
Self::new_from_span(message, span)
}
pub fn multiple_definition(value: String, span: Span) -> Self {
let message = format!("cannot assign multiple variables to a single value: {}", value,);
Self::new_from_span(message, span)
}
pub fn select_fail(first: String, second: String, span: Span) -> Self {
let message = format!(
"Conditional select gadget failed to select between `{}` or `{}`",

View File

@ -1,7 +1,7 @@
//! Enforces a definition statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, ConstrainedValue, GroupType};
use leo_typed::{Declare, Expression, Span, VariableName, Variables};
use leo_typed::{Declare, Expression, Span, Type, VariableName, Variables};
use snarkos_models::{
curves::{Field, PrimeField},
@ -29,6 +29,82 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Ok(())
}
fn enforce_expressions<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
types: Vec<Type>,
expressions: Vec<Expression>,
) -> Result<Vec<ConstrainedValue<F, G>>, StatementError> {
let implicit_types = types.is_empty();
let mut expected_types = vec![];
for i in 0..expressions.len() {
let expected_type = if implicit_types { vec![] } else { vec![types[i].clone()] };
expected_types.push(expected_type);
}
let mut values = vec![];
for (expression, expected_type) in expressions.into_iter().zip(expected_types.into_iter()) {
let value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_type,
expression,
)?;
values.push(value);
}
Ok(values)
}
fn enforce_tuple_definition<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
is_constant: bool,
variables: Variables,
expressions: Vec<Expression>,
span: Span,
) -> Result<(), StatementError> {
let values = self.enforce_expressions(cs, file_scope, function_scope.clone(), variables.types, expressions)?;
let tuple = ConstrainedValue::Tuple(values);
let variable = variables.names[0].clone();
self.enforce_single_definition(cs, function_scope, is_constant, variable, tuple, span)
}
fn enforce_multiple_definition<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
function_scope: String,
is_constant: bool,
variables: Variables,
values: Vec<ConstrainedValue<F, G>>,
span: Span,
) -> Result<(), StatementError> {
if values.len() != variables.names.len() {
return Err(StatementError::invalid_number_of_definitions(
values.len(),
variables.names.len(),
span,
));
}
for (variable, value) in variables.names.into_iter().zip(values.into_iter()) {
self.enforce_single_definition(cs, function_scope.clone(), is_constant, variable, value, span.clone())?;
}
Ok(())
}
pub fn enforce_definition_statement<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
@ -39,60 +115,67 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
expressions: Vec<Expression>,
span: Span,
) -> Result<(), StatementError> {
let num_variables = variables.names.len();
let num_values = expressions.len();
let is_constant = match declare {
Declare::Let => false,
Declare::Const => true,
};
if variables.names.len() == 1 && expressions.len() == 1 {
if num_variables == 1 && num_values == 1 {
// Define a single variable with a single value
let variable = variables.names[0].clone();
let expression = self.enforce_expression(
let expression = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&variables.types,
expressions[0].clone(),
)?;
)? {
ConstrainedValue::Return(values) => ConstrainedValue::Tuple(values),
value => value,
};
self.enforce_single_definition(cs, function_scope, is_constant, variable, expression, span)
} else if variables.names.len() == 1 && expressions.len() > 1 {
} else if num_variables == 1 && num_values > 1 {
// Define a tuple (single variable with multiple values)
let implicit_types = variables.types.is_empty();
let mut expected_types = vec![];
self.enforce_tuple_definition(
cs,
file_scope,
function_scope,
is_constant,
variables,
expressions,
span,
)
} else if num_variables > 1 && num_values == 1 {
// Define multiple variables for an expression that returns multiple results (multiple definition)
for i in 0..expressions.len() {
let expected_type = if implicit_types {
vec![]
} else {
vec![variables.types[i].clone()]
};
let values = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&variables.types,
expressions[0].clone(),
)? {
ConstrainedValue::Return(values) => values,
value => return Err(StatementError::multiple_definition(value.to_string(), span.clone())),
};
expected_types.push(expected_type);
}
let mut values = vec![];
for (expression, expected_type) in expressions.into_iter().zip(expected_types.into_iter()) {
let value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_type,
expression,
)?;
values.push(value);
}
let tuple = ConstrainedValue::Tuple(values);
let variable = variables.names[0].clone();
self.enforce_single_definition(cs, function_scope, is_constant, variable, tuple, span)
self.enforce_multiple_definition(cs, function_scope, is_constant, variables, values, span)
} else {
Ok(())
// Define multiple variables for multiple expressions
let values = self.enforce_expressions(
cs,
file_scope,
function_scope.clone(),
variables.types.clone(),
expressions,
)?;
self.enforce_multiple_definition(cs, function_scope, is_constant, variables, values, span)
}
}
}

View File

@ -295,7 +295,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> fmt::Display for ConstrainedValue<F
write!(f, "]")
}
ConstrainedValue::Tuple(ref tuple) => {
let values = tuple.iter().map(|x| format!("{}", x)).collect::<Vec<_>>().join(",");
let values = tuple.iter().map(|x| format!("{}", x)).collect::<Vec<_>>().join(", ");
write!(f, "({})", values)
}
@ -366,6 +366,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConditionalEqGadget<F> for Constrai
}
Ok(())
}
(ConstrainedValue::Tuple(tuple_1), ConstrainedValue::Tuple(tuple_2)) => {
for (i, (left, right)) in tuple_1.into_iter().zip(tuple_2.into_iter()).enumerate() {
left.conditional_enforce_equal(cs.ns(|| format!("tuple index {}", i)), right, condition)?;
}
Ok(())
}
(_, _) => return Err(SynthesisError::Unsatisfiable),
}
}
@ -412,6 +418,20 @@ impl<F: Field + PrimeField, G: GroupType<F>> CondSelectGadget<F> for Constrained
ConstrainedValue::Array(array)
}
(ConstrainedValue::Tuple(tuple_1), ConstrainedValue::Array(tuple_2)) => {
let mut array = vec![];
for (i, (first, second)) in tuple_1.into_iter().zip(tuple_2.into_iter()).enumerate() {
array.push(Self::conditionally_select(
cs.ns(|| format!("tuple index {}", i)),
cond,
first,
second,
)?);
}
ConstrainedValue::Tuple(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