support tuple parsing and assignment

This commit is contained in:
collin 2020-08-10 18:21:23 -07:00
parent a84875ea68
commit e85d6499d0
17 changed files with 224 additions and 97 deletions

View File

@ -5,7 +5,7 @@ use crate::{
value::ConstrainedValue,
GroupType,
};
use leo_typed::Variable;
use leo_typed::Identifier;
use snarkos_models::curves::{Field, PrimeField};
@ -13,15 +13,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub fn store_definition(
&mut self,
function_scope: String,
variable: Variable,
mutable: bool,
identifier: Identifier,
mut value: ConstrainedValue<F, G>,
) -> () {
// Store with given mutability
if variable.mutable {
if mutable {
value = ConstrainedValue::Mutable(Box::new(value));
}
let variable_program_identifier = new_scope(function_scope, variable.identifier.name);
let variable_program_identifier = new_scope(function_scope, identifier.name);
self.store(variable_program_identifier, value);
}

View File

@ -82,6 +82,12 @@ impl ExpressionError {
Self::new_from_span(message, span)
}
pub fn index_out_of_bounds(index: usize, span: Span) -> Self {
let message = format!("cannot access index {} of tuple out of bounds", index);
Self::new_from_span(message, span)
}
pub fn invalid_index(actual: String, span: Span) -> Self {
let message = format!("index must resolve to an integer, found `{}`", actual);

View File

@ -122,6 +122,12 @@ impl StatementError {
Self::new_from_span(message, span)
}
pub fn tuple_assign_index(span: Span) -> Self {
let message = format!("Cannot assign single index to tuple of values");
Self::new_from_span(message, span)
}
pub fn unassigned(name: String, span: Span) -> Self {
let message = format!("Expected assignment of return values for expression `{}`", name);

View File

@ -234,6 +234,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
self.enforce_array_access(cs, file_scope, function_scope, expected_types, array, *index, span)
}
// Tuples
Expression::TupleAccess(tuple, index, span) => {
self.enforce_tuple_access(cs, file_scope, function_scope, expected_types, tuple, index, span)
}
// Circuits
Expression::Circuit(circuit_name, members, span) => {
self.enforce_circuit(cs, file_scope, function_scope, circuit_name, members, span)

View File

@ -29,3 +29,6 @@ pub use self::logical::*;
pub mod relational;
pub use self::relational::*;
pub mod tuple;
pub use self::tuple::*;

View File

@ -0,0 +1,40 @@
//! Enforces array access in a compiled Leo program.
use crate::{errors::ExpressionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_typed::{Expression, Span, Type};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub fn enforce_tuple_access<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: &Vec<Type>,
tuple: Box<Expression>,
index: usize,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let tuple = match self.enforce_operand(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*tuple,
span.clone(),
)? {
ConstrainedValue::Tuple(tuple) => tuple,
value => return Err(ExpressionError::undefined_array(value.to_string(), span.clone())),
};
if index > tuple.len() - 1 {
return Err(ExpressionError::index_out_of_bounds(index, span));
}
Ok(tuple[index].to_owned())
}
}

View File

@ -0,0 +1,4 @@
//! Methods to enforce tuple expressions in a compiled Leo program.
pub mod access;
pub use self::access::*;

View File

@ -62,6 +62,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
new_value,
span,
),
Assignee::Tuple(_tuple, index) => self.assign_tuple(cs, indicator, variable_name, index, new_value, span),
Assignee::CircuitField(_assignee, object_name) => {
self.mutute_circuit_field(cs, indicator, variable_name, object_name, new_value, span)
}

View File

@ -9,6 +9,7 @@ pub fn resolve_assignee(scope: String, assignee: Assignee) -> String {
match assignee {
Assignee::Identifier(name) => new_scope(scope, name.to_string()),
Assignee::Array(array, _index) => resolve_assignee(scope, *array),
Assignee::Tuple(tuple, _index) => resolve_assignee(scope, *tuple),
Assignee::CircuitField(circuit_name, _member) => resolve_assignee(scope, *circuit_name),
}
}

View File

@ -11,3 +11,6 @@ pub use self::assignee::*;
pub mod circuit_field;
pub use self::circuit_field::*;
pub mod tuple;
pub use self::tuple::*;

View File

@ -0,0 +1,45 @@
//! Enforces a tuple assignment statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_typed::Span;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{boolean::Boolean, select::CondSelectGadget},
},
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub fn assign_tuple<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
indicator: Option<Boolean>,
name: String,
index: usize,
mut new_value: ConstrainedValue<F, G>,
span: Span,
) -> Result<(), StatementError> {
let condition = indicator.unwrap_or(Boolean::Constant(true));
// Modify the single value of the tuple in place
match self.get_mutable_assignee(name, span.clone())? {
ConstrainedValue::Tuple(old) => {
new_value.resolve_type(&vec![old[index].to_type(span.clone())?], span.clone())?;
let name_unique = format!("select {} {}:{}", new_value, span.line, span.start);
let selected_value =
ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &new_value, &old[index])
.map_err(|_| {
StatementError::select_fail(new_value.to_string(), old[index].to_string(), span)
})?;
old[index] = selected_value;
}
_ => return Err(StatementError::tuple_assign_index(span)),
}
Ok(())
}
}

View File

@ -1,7 +1,7 @@
//! Enforces a definition statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, GroupType};
use leo_typed::{Declare, Expression, Span, Variable};
use crate::{errors::StatementError, program::ConstrainedProgram, ConstrainedValue, GroupType};
use leo_typed::{Declare, Expression, Span, VariableName, Variables};
use snarkos_models::{
curves::{Field, PrimeField},
@ -9,39 +9,90 @@ use snarkos_models::{
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
fn enforce_single_definition<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
function_scope: String,
is_constant: bool,
variable_name: VariableName,
mut value: ConstrainedValue<F, G>,
span: Span,
) -> Result<(), StatementError> {
if is_constant && variable_name.mutable {
return Err(StatementError::immutable_assign(variable_name.to_string(), span));
} else {
value.allocate_value(cs, span)?
}
self.store_definition(function_scope, variable_name.mutable, variable_name.identifier, value);
Ok(())
}
pub fn enforce_definition_statement<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
declare: Declare,
variable: Variable,
expression: Expression,
variables: Variables,
expressions: Vec<Expression>,
span: Span,
) -> Result<(), StatementError> {
let mut expected_types = vec![];
if let Some(ref _type) = variable._type {
expected_types.push(_type.clone());
}
let mut value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_types,
expression,
)?;
let is_constant = match declare {
Declare::Let => false,
Declare::Const => true,
};
match declare {
Declare::Let => value.allocate_value(cs, span)?,
Declare::Const => {
if variable.mutable {
return Err(StatementError::immutable_assign(variable.to_string(), span));
}
if variables.names.len() == 1 && expressions.len() == 1 {
// Define a single variable with a single value
let variable = variables.names[0].clone();
let expression = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&variables.types,
expressions[0].clone(),
)?;
self.enforce_single_definition(cs, function_scope, is_constant, variable, expression, span)
} else if variables.names.len() == 1 && expressions.len() > 1 {
// Define a tuple (single variable with multiple values)
let implicit_types = variables.types.is_empty();
let mut expected_types = vec![];
for i in 0..expressions.len() {
let expected_type = if implicit_types {
vec![]
} else {
vec![variables.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);
}
let tuple = ConstrainedValue::Tuple(values);
let variable = variables.names[0].clone();
self.enforce_single_definition(cs, function_scope, is_constant, variable, tuple, span)
} else {
Ok(())
}
self.store_definition(function_scope, variable, value);
Ok(())
}
}

View File

@ -2,6 +2,3 @@
pub mod definition;
pub use self::definition::*;
pub mod multiple;
pub use self::multiple::*;

View File

@ -1,54 +0,0 @@
//! Enforces a multiple definition statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_typed::{Expression, Span, Variable};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub fn enforce_multiple_definition_statement<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
variables: Vec<Variable>,
function: Expression,
span: Span,
) -> Result<(), StatementError> {
let mut expected_types = vec![];
for variable in variables.iter() {
if let Some(ref _type) = variable._type {
expected_types.push(_type.clone());
}
}
// Expect return values from function
let return_values = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_types,
function,
)? {
ConstrainedValue::Return(values) => values,
value => unimplemented!("multiple assignment only implemented for functions, got {}", value),
};
if variables.len() != return_values.len() {
return Err(StatementError::invalid_number_of_definitions(
variables.len(),
return_values.len(),
span,
));
}
for (variable, value) in variables.into_iter().zip(return_values.into_iter()) {
self.store_definition(function_scope.clone(), variable, value);
}
Ok(())
}
}

View File

@ -34,11 +34,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
results.push(return_value);
}
Statement::Definition(declare, variable, expression, span) => {
self.enforce_definition_statement(cs, file_scope, function_scope, declare, variable, expression, span)?;
}
Statement::MultipleDefinition(variables, function, span) => {
self.enforce_multiple_definition_statement(cs, file_scope, function_scope, variables, function, span)?;
Statement::Definition(declare, variables, expressions, span) => {
self.enforce_definition_statement(
cs,
file_scope,
function_scope,
declare,
variables,
expressions,
span,
)?;
}
Statement::Assign(variable, expression, span) => {
self.enforce_assign_statement(cs, file_scope, function_scope, indicator, variable, expression, span)?;

View File

@ -37,6 +37,9 @@ pub enum ConstrainedValue<F: Field + PrimeField, G: GroupType<F>> {
// Arrays
Array(Vec<ConstrainedValue<F, G>>),
// Tuples
Tuple(Vec<ConstrainedValue<F, G>>),
// Circuits
CircuitDefinition(Circuit),
CircuitExpression(Identifier, Vec<ConstrainedCircuitMember<F, G>>),
@ -208,6 +211,17 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
})
.collect::<Result<(), ValueError>>()?;
}
ConstrainedValue::Tuple(tuple) => {
tuple
.iter_mut()
.enumerate()
.map(|(i, value)| {
let unique_name = format!("allocate tuple member {} {}:{}", i, span.line, span.start);
value.allocate_value(cs.ns(|| unique_name), span.clone())
})
.collect::<Result<(), ValueError>>()?;
}
ConstrainedValue::CircuitExpression(_id, members) => {
members
.iter_mut()
@ -280,6 +294,11 @@ 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(",");
write!(f, "({})", values)
}
ConstrainedValue::CircuitExpression(ref identifier, ref members) => {
write!(f, "{} {{", identifier)?;
for (i, member) in members.iter().enumerate() {

View File

@ -69,7 +69,6 @@ pub enum Expression {
ArrayAccess(Box<Expression>, Box<RangeOrExpression>, Span), // (array name, range)
// Tuples
Tuple(Vec<Expression>, Span),
TupleAccess(Box<Expression>, usize, Span),
// Circuits
@ -183,11 +182,6 @@ impl<'ast> fmt::Display for Expression {
Expression::ArrayAccess(ref array, ref index, ref _span) => write!(f, "{}[{}]", array, index),
// Tuples
Expression::Tuple(ref tuple, ref _span) => {
let values = tuple.iter().map(|x| format!("{}", x)).collect::<Vec<_>>().join(",");
write!(f, "({})", values)
}
Expression::TupleAccess(ref tuple, ref index, ref _span) => write!(f, "{}.{}", tuple, index),
// Circuits