diff --git a/compiler/src/statement/assign/array.rs b/compiler/src/statement/assign/array.rs new file mode 100644 index 0000000000..cf9706a2d7 --- /dev/null +++ b/compiler/src/statement/assign/array.rs @@ -0,0 +1,94 @@ +//! Enforces an array assignment statement in a compiled Leo program. + +use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType}; +use leo_types::{RangeOrExpression, Span}; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::{ + r1cs::ConstraintSystem, + utilities::{boolean::Boolean, select::CondSelectGadget}, + }, +}; + +impl> ConstrainedProgram { + pub fn assign_array>( + &mut self, + cs: &mut CS, + file_scope: String, + function_scope: String, + indicator: Option, + name: String, + range_or_expression: RangeOrExpression, + mut new_value: ConstrainedValue, + span: Span, + ) -> 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 + match range_or_expression { + RangeOrExpression::Expression(index) => { + let index = self.enforce_index(cs, file_scope.clone(), function_scope.clone(), index, span.clone())?; + + // Modify the single value of the array in place + match self.get_mutable_assignee(name, span.clone())? { + ConstrainedValue::Array(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::array_assign_index(span)), + } + } + RangeOrExpression::Range(from, to) => { + let from_index = match from { + Some(integer) => { + self.enforce_index(cs, file_scope.clone(), function_scope.clone(), integer, span.clone())? + } + None => 0usize, + }; + let to_index_option = match to { + Some(integer) => Some(self.enforce_index( + cs, + file_scope.clone(), + function_scope.clone(), + integer, + span.clone(), + )?), + None => None, + }; + + // Modify the range of values of the array + let old_array = self.get_mutable_assignee(name, span.clone())?; + let new_array = match (old_array.clone(), new_value) { + (ConstrainedValue::Array(mut mutable), ConstrainedValue::Array(new)) => { + let to_index = to_index_option.unwrap_or(mutable.len()); + + mutable.splice(from_index..to_index, new.iter().cloned()); + ConstrainedValue::Array(mutable) + } + _ => return Err(StatementError::array_assign_range(span)), + }; + let name_unique = format!("select {} {}:{}", new_array, span.line, span.start); + let selected_array = + ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &new_array, old_array) + .map_err(|_| StatementError::select_fail(new_array.to_string(), old_array.to_string(), span))?; + + *old_array = selected_array; + } + } + + Ok(()) + } +} diff --git a/compiler/src/statement/assign/assign.rs b/compiler/src/statement/assign/assign.rs new file mode 100644 index 0000000000..693bb8f4b8 --- /dev/null +++ b/compiler/src/statement/assign/assign.rs @@ -0,0 +1,70 @@ +//! Enforces an assign statement in a compiled Leo program. + +use crate::{ + assignee::resolve_assignee, + errors::StatementError, + program::ConstrainedProgram, + value::ConstrainedValue, + GroupType, +}; +use leo_types::{Assignee, Expression, Span}; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::{ + r1cs::ConstraintSystem, + utilities::{boolean::Boolean, select::CondSelectGadget}, + }, +}; + +impl> ConstrainedProgram { + pub fn enforce_assign_statement>( + &mut self, + cs: &mut CS, + file_scope: String, + function_scope: String, + indicator: Option, + assignee: Assignee, + expression: Expression, + span: Span, + ) -> Result<(), StatementError> { + // Get the name of the variable we are assigning to + let variable_name = resolve_assignee(function_scope.clone(), assignee.clone()); + + // Evaluate new value + let mut new_value = + self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), &vec![], expression)?; + + // Mutate the old value into the new value + match assignee { + Assignee::Identifier(_identifier) => { + let condition = indicator.unwrap_or(Boolean::Constant(true)); + let old_value = self.get_mutable_assignee(variable_name.clone(), span.clone())?; + + new_value.resolve_type(&vec![old_value.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_value) + .map_err(|_| StatementError::select_fail(new_value.to_string(), old_value.to_string(), span))?; + + *old_value = selected_value; + + Ok(()) + } + Assignee::Array(_assignee, range_or_expression) => self.assign_array( + cs, + file_scope, + function_scope, + indicator, + variable_name, + range_or_expression, + new_value, + span, + ), + Assignee::CircuitField(_assignee, object_name) => { + self.mutute_circuit_field(cs, indicator, variable_name, object_name, new_value, span) + } + } + } +} diff --git a/compiler/src/statement/assign/assignee.rs b/compiler/src/statement/assign/assignee.rs new file mode 100644 index 0000000000..32a3357aeb --- /dev/null +++ b/compiler/src/statement/assign/assignee.rs @@ -0,0 +1,31 @@ +//! Resolves assignees in a compiled Leo program. + +use crate::{errors::StatementError, new_scope, program::ConstrainedProgram, value::ConstrainedValue, GroupType}; +use leo_types::{Assignee, Span}; + +use snarkos_models::curves::{Field, PrimeField}; + +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::CircuitField(circuit_name, _member) => resolve_assignee(scope, *circuit_name), + } +} + +impl> ConstrainedProgram { + pub fn get_mutable_assignee( + &mut self, + name: String, + span: Span, + ) -> Result<&mut ConstrainedValue, StatementError> { + // Check that assignee exists and is mutable + Ok(match self.get_mut(&name) { + Some(value) => match value { + ConstrainedValue::Mutable(mutable_value) => mutable_value, + _ => return Err(StatementError::immutable_assign(name, span)), + }, + None => return Err(StatementError::undefined_variable(name, span)), + }) + } +} diff --git a/compiler/src/statement/assign/circuit_field.rs b/compiler/src/statement/assign/circuit_field.rs new file mode 100644 index 0000000000..4fb864b3f8 --- /dev/null +++ b/compiler/src/statement/assign/circuit_field.rs @@ -0,0 +1,67 @@ +//! Enforces a circuit field assignment statement in a compiled Leo program. + +use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType}; +use leo_types::{Identifier, Span}; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::{ + r1cs::ConstraintSystem, + utilities::{boolean::Boolean, select::CondSelectGadget}, + }, +}; + +impl> ConstrainedProgram { + pub fn mutute_circuit_field>( + &mut self, + cs: &mut CS, + indicator: Option, + circuit_name: String, + object_name: Identifier, + mut new_value: ConstrainedValue, + span: Span, + ) -> Result<(), StatementError> { + let condition = indicator.unwrap_or(Boolean::Constant(true)); + + match self.get_mutable_assignee(circuit_name, span.clone())? { + ConstrainedValue::CircuitExpression(_variable, members) => { + // Modify the circuit value.field in place + let matched_field = members.into_iter().find(|object| object.0 == object_name); + + match matched_field { + Some(object) => match &object.1 { + ConstrainedValue::Function(_circuit_identifier, function) => { + return Err(StatementError::immutable_circuit_function( + function.function_name.to_string(), + span, + )); + } + ConstrainedValue::Static(_value) => { + return Err(StatementError::immutable_circuit_function("static".into(), span)); + } + _ => { + new_value.resolve_type(&vec![object.1.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, + &object.1, + ) + .map_err(|_| { + StatementError::select_fail(new_value.to_string(), object.1.to_string(), span) + })?; + + object.1 = selected_value.to_owned(); + } + }, + None => return Err(StatementError::undefined_circuit_object(object_name.to_string(), span)), + } + } + _ => return Err(StatementError::undefined_circuit(object_name.to_string(), span)), + } + + Ok(()) + } +} diff --git a/compiler/src/statement/assign/mod.rs b/compiler/src/statement/assign/mod.rs new file mode 100644 index 0000000000..de0c9aa8b7 --- /dev/null +++ b/compiler/src/statement/assign/mod.rs @@ -0,0 +1,13 @@ +//! Methods to enforce constraints on assign statements in a compiled Leo program. + +pub mod array; +pub use self::array::*; + +pub mod assign; +pub use self::assign::*; + +pub mod assignee; +pub use self::assignee::*; + +pub mod circuit_field; +pub use self::circuit_field::*; diff --git a/compiler/src/statement/mod.rs b/compiler/src/statement/mod.rs index 8878537588..2a54ecf306 100644 --- a/compiler/src/statement/mod.rs +++ b/compiler/src/statement/mod.rs @@ -1,3 +1,6 @@ +pub mod assign; +pub use self::assign::*; + pub mod definition; pub use self::definition::*; diff --git a/compiler/src/statement/statement.rs b/compiler/src/statement/statement.rs index 7795fa6cc9..ed5607fe22 100644 --- a/compiler/src/statement/statement.rs +++ b/compiler/src/statement/statement.rs @@ -8,233 +8,17 @@ use crate::{ GroupType, Integer, }; -use leo_types::{ - Assignee, - ConditionalNestedOrEndStatement, - ConditionalStatement, - Expression, - Identifier, - RangeOrExpression, - Span, - Statement, - Type, -}; +use leo_types::{ConditionalNestedOrEndStatement, ConditionalStatement, Expression, Identifier, Span, Statement, Type}; use snarkos_models::{ curves::{Field, PrimeField}, gadgets::{ r1cs::ConstraintSystem, - utilities::{boolean::Boolean, eq::ConditionalEqGadget, select::CondSelectGadget, uint::UInt32}, + utilities::{boolean::Boolean, eq::ConditionalEqGadget, uint::UInt32}, }, }; impl> ConstrainedProgram { - fn resolve_assignee(&mut self, scope: String, assignee: Assignee) -> String { - match assignee { - Assignee::Identifier(name) => new_scope(scope, name.to_string()), - Assignee::Array(array, _index) => self.resolve_assignee(scope, *array), - Assignee::CircuitField(circuit_name, _member) => self.resolve_assignee(scope, *circuit_name), - } - } - - fn get_mutable_assignee( - &mut self, - name: String, - span: Span, - ) -> Result<&mut ConstrainedValue, StatementError> { - // Check that assignee exists and is mutable - Ok(match self.get_mut(&name) { - Some(value) => match value { - ConstrainedValue::Mutable(mutable_value) => mutable_value, - _ => return Err(StatementError::immutable_assign(name, span)), - }, - None => return Err(StatementError::undefined_variable(name, span)), - }) - } - - fn mutate_array>( - &mut self, - cs: &mut CS, - file_scope: String, - function_scope: String, - indicator: Option, - name: String, - range_or_expression: RangeOrExpression, - mut new_value: ConstrainedValue, - span: Span, - ) -> 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 - match range_or_expression { - RangeOrExpression::Expression(index) => { - let index = self.enforce_index(cs, file_scope.clone(), function_scope.clone(), index, span.clone())?; - - // Modify the single value of the array in place - match self.get_mutable_assignee(name, span.clone())? { - ConstrainedValue::Array(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::array_assign_index(span)), - } - } - RangeOrExpression::Range(from, to) => { - let from_index = match from { - Some(integer) => { - self.enforce_index(cs, file_scope.clone(), function_scope.clone(), integer, span.clone())? - } - None => 0usize, - }; - let to_index_option = match to { - Some(integer) => Some(self.enforce_index( - cs, - file_scope.clone(), - function_scope.clone(), - integer, - span.clone(), - )?), - None => None, - }; - - // Modify the range of values of the array - let old_array = self.get_mutable_assignee(name, span.clone())?; - let new_array = match (old_array.clone(), new_value) { - (ConstrainedValue::Array(mut mutable), ConstrainedValue::Array(new)) => { - let to_index = to_index_option.unwrap_or(mutable.len()); - - mutable.splice(from_index..to_index, new.iter().cloned()); - ConstrainedValue::Array(mutable) - } - _ => return Err(StatementError::array_assign_range(span)), - }; - let name_unique = format!("select {} {}:{}", new_array, span.line, span.start); - let selected_array = - ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &new_array, old_array) - .map_err(|_| StatementError::select_fail(new_array.to_string(), old_array.to_string(), span))?; - - *old_array = selected_array; - } - } - - Ok(()) - } - - fn mutute_circuit_field>( - &mut self, - cs: &mut CS, - indicator: Option, - circuit_name: String, - object_name: Identifier, - mut new_value: ConstrainedValue, - span: Span, - ) -> Result<(), StatementError> { - let condition = indicator.unwrap_or(Boolean::Constant(true)); - - match self.get_mutable_assignee(circuit_name, span.clone())? { - ConstrainedValue::CircuitExpression(_variable, members) => { - // Modify the circuit value.field in place - let matched_field = members.into_iter().find(|object| object.0 == object_name); - - match matched_field { - Some(object) => match &object.1 { - ConstrainedValue::Function(_circuit_identifier, function) => { - return Err(StatementError::immutable_circuit_function( - function.function_name.to_string(), - span, - )); - } - ConstrainedValue::Static(_value) => { - return Err(StatementError::immutable_circuit_function("static".into(), span)); - } - _ => { - new_value.resolve_type(&vec![object.1.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, - &object.1, - ) - .map_err(|_| { - StatementError::select_fail(new_value.to_string(), object.1.to_string(), span) - })?; - - object.1 = selected_value.to_owned(); - } - }, - None => return Err(StatementError::undefined_circuit_object(object_name.to_string(), span)), - } - } - _ => return Err(StatementError::undefined_circuit(object_name.to_string(), span)), - } - - Ok(()) - } - - fn enforce_assign_statement>( - &mut self, - cs: &mut CS, - file_scope: String, - function_scope: String, - indicator: Option, - assignee: Assignee, - expression: Expression, - span: Span, - ) -> Result<(), StatementError> { - // Get the name of the variable we are assigning to - let variable_name = self.resolve_assignee(function_scope.clone(), assignee.clone()); - - // Evaluate new value - let mut new_value = - self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), &vec![], expression)?; - - // Mutate the old value into the new value - match assignee { - Assignee::Identifier(_identifier) => { - let condition = indicator.unwrap_or(Boolean::Constant(true)); - let old_value = self.get_mutable_assignee(variable_name.clone(), span.clone())?; - - new_value.resolve_type(&vec![old_value.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_value) - .map_err(|_| StatementError::select_fail(new_value.to_string(), old_value.to_string(), span))?; - - *old_value = selected_value; - - Ok(()) - } - Assignee::Array(_assignee, range_or_expression) => self.mutate_array( - cs, - file_scope, - function_scope, - indicator, - variable_name, - range_or_expression, - new_value, - span, - ), - Assignee::CircuitField(_assignee, object_name) => { - self.mutute_circuit_field(cs, indicator, variable_name, object_name, new_value, span) - } - } - } - fn evaluate_branch>( &mut self, cs: &mut CS, @@ -457,12 +241,12 @@ impl> ConstrainedProgram { 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::Assign(variable, expression, span) => { self.enforce_assign_statement(cs, file_scope, function_scope, indicator, variable, expression, span)?; } - Statement::MultipleAssign(variables, function, span) => { - self.enforce_multiple_definition_statement(cs, file_scope, function_scope, variables, function, span)?; - } Statement::Conditional(statement, span) => { let mut result = self.enforce_conditional_statement( cs, diff --git a/types/src/statements/statement.rs b/types/src/statements/statement.rs index bb3785e59c..31c018f534 100644 --- a/types/src/statements/statement.rs +++ b/types/src/statements/statement.rs @@ -22,8 +22,8 @@ use std::fmt; pub enum Statement { Return(Vec, Span), Definition(Declare, Variable, Expression, Span), + MultipleDefinition(Vec, Expression, Span), Assign(Assignee, Expression, Span), - MultipleAssign(Vec, Expression, Span), Conditional(ConditionalStatement, Span), For(Identifier, Expression, Expression, Vec, Span), AssertEq(Expression, Expression, Span), @@ -140,7 +140,7 @@ impl<'ast> From> for Statement { .map(|typed_variable| Variable::from(typed_variable)) .collect(); - Statement::MultipleAssign( + Statement::MultipleDefinition( variables, Expression::FunctionCall( Box::new(Expression::from(statement.function_name)), @@ -226,7 +226,7 @@ impl fmt::Display for Statement { write!(f, "{} {} = {};", declare, variable, expression) } Statement::Assign(ref variable, ref statement, ref _span) => write!(f, "{} = {};", variable, statement), - Statement::MultipleAssign(ref assignees, ref function, ref _span) => { + Statement::MultipleDefinition(ref assignees, ref function, ref _span) => { write!(f, "let (")?; for (i, id) in assignees.iter().enumerate() { write!(f, "{}", id)?;