impl mutable circuit and arrays

This commit is contained in:
collin 2020-05-13 17:52:50 -07:00
parent 3b263d7cc9
commit 9f1803f6da
6 changed files with 172 additions and 135 deletions

View File

@ -2,6 +2,18 @@ circuit Circ {
x: u32
}
function main() {
let mut c = Circ { x: 4 };
function main() -> u32 {
let mut a = 1;
a = 0;
let b = 1;
//b = 0; // <- illegal
let mut arr = [1, 2];
arr[0] = 0;
let mut c = Circ { x: 1 };
c.x = 0;
return c.x
}

View File

@ -2,7 +2,7 @@
use crate::{
constraints::{
new_scope_from_variable, new_variable_from_variable, ConstrainedCircuitMember,
new_scope_from_variable, new_variable_from_variable, ConstrainedCircuitObject,
ConstrainedProgram, ConstrainedValue,
},
errors::ExpressionError,
@ -308,30 +308,38 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
array: Box<Expression<F, G>>,
index: RangeOrExpression<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *array)? {
ConstrainedValue::Array(array) => {
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
Some(from_index) => from_index.to_usize(),
None => 0usize, // Array slice starts at index 0
};
let to_resolved = match to {
Some(to_index) => to_index.to_usize(),
None => array.len(), // Array slice ends at array length
};
Ok(ConstrainedValue::Array(
array[from_resolved..to_resolved].to_owned(),
))
}
RangeOrExpression::Expression(index) => {
let index_resolved =
self.enforce_index(cs, file_scope, function_scope, index)?;
Ok(array[index_resolved].to_owned())
}
}
let array = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*array,
)? {
ConstrainedValue::Array(array) => array,
ConstrainedValue::Mutable(value) => match *value {
ConstrainedValue::Array(array) => array,
value => return Err(ExpressionError::InvalidArrayAccess(value.to_string())),
},
value => return Err(ExpressionError::InvalidArrayAccess(value.to_string())),
};
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
Some(from_index) => from_index.to_usize(),
None => 0usize, // Array slice starts at index 0
};
let to_resolved = match to {
Some(to_index) => to_index.to_usize(),
None => array.len(), // Array slice ends at array length
};
Ok(ConstrainedValue::Array(
array[from_resolved..to_resolved].to_owned(),
))
}
RangeOrExpression::Expression(index) => {
let index_resolved = self.enforce_index(cs, file_scope, function_scope, index)?;
Ok(array[index_resolved].to_owned())
}
value => Err(ExpressionError::InvalidArrayAccess(value.to_string())),
}
}
@ -372,7 +380,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// Check member types
member_value.expect_type(&field._type)?;
resolved_members.push(ConstrainedCircuitMember(member.identifier, member_value))
resolved_members.push(ConstrainedCircuitObject(member.identifier, member_value))
}
Ok(ConstrainedValue::CircuitExpression(
@ -392,19 +400,29 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
circuit_variable: Box<Expression<F, G>>,
circuit_member: Identifier<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match self.enforce_expression(cs, file_scope, function_scope, *circuit_variable)? {
ConstrainedValue::CircuitExpression(_name, members) => {
let matched_member = members
.into_iter()
.find(|member| member.0 == circuit_member);
match matched_member {
Some(member) => Ok(member.1),
None => Err(ExpressionError::UndefinedCircuitObject(
circuit_member.to_string(),
)),
}
}
value => Err(ExpressionError::InvalidCircuitAccess(value.to_string())),
let members = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*circuit_variable.clone(),
)? {
ConstrainedValue::CircuitExpression(_name, members) => members,
ConstrainedValue::Mutable(value) => match *value {
ConstrainedValue::CircuitExpression(_name, members) => members,
value => return Err(ExpressionError::InvalidCircuitAccess(value.to_string())),
},
value => return Err(ExpressionError::InvalidCircuitAccess(value.to_string())),
};
let matched_member = members
.into_iter()
.find(|member| member.0 == circuit_member);
match matched_member {
Some(member) => Ok(member.1),
None => Err(ExpressionError::UndefinedCircuitObject(
circuit_member.to_string(),
)),
}
}

View File

@ -27,95 +27,46 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
}
fn store_assignment(
fn mutate_array(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
assignee: Assignee<F, G>,
return_value: &mut ConstrainedValue<F, G>,
name: String,
range_or_expression: RangeOrExpression<F, G>,
new_value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
match assignee {
Assignee::Identifier(name) => {
// Store the variable in the current scope
let definition_name = new_scope_from_variable(function_scope.clone(), &name);
// 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)?;
self.store(definition_name, return_value.to_owned());
}
Assignee::Array(array, index_expression) => {
// Check that array exists
let expected_array_name = self.resolve_assignee(function_scope.clone(), *array);
// Resolve index so we know if we are assigning to a single value or a range of values
match index_expression {
RangeOrExpression::Expression(index) => {
let index = self.enforce_index(
cs,
file_scope.clone(),
function_scope.clone(),
index,
)?;
// Modify the single value of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match value {
ConstrainedValue::Array(old) => {
old[index] = return_value.to_owned();
}
_ => return Err(StatementError::ArrayAssignIndex),
},
None => {
return Err(StatementError::UndefinedArray(expected_array_name))
}
}
}
RangeOrExpression::Range(from, to) => {
let from_index = match from {
Some(integer) => integer.to_usize(),
None => 0usize,
};
let to_index_option = match to {
Some(integer) => Some(integer.to_usize()),
None => None,
};
// Modify the range of values of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match (value, return_value) {
(ConstrainedValue::Array(old), ConstrainedValue::Array(new)) => {
let to_index = to_index_option.unwrap_or(old.len());
old.splice(from_index..to_index, new.iter().cloned());
}
_ => return Err(StatementError::ArrayAssignRange),
},
None => {
return Err(StatementError::UndefinedArray(expected_array_name))
}
}
// Modify the single value of the array in place
match self.get_mutable_variable(name)? {
ConstrainedValue::Array(old) => {
old[index] = new_value;
}
_ => return Err(StatementError::ArrayAssignIndex),
}
}
Assignee::CircuitMember(circuit_variable, circuit_member) => {
// Check that circuit exists
let expected_circuit_name =
self.resolve_assignee(function_scope.clone(), *circuit_variable);
RangeOrExpression::Range(from, to) => {
let from_index = match from {
Some(integer) => integer.to_usize(),
None => 0usize,
};
let to_index_option = match to {
Some(integer) => Some(integer.to_usize()),
None => None,
};
match self.get_mut(&expected_circuit_name) {
Some(ConstrainedValue::CircuitExpression(_variable, members)) => {
// Modify the circuit member in place
let matched_member = members
.into_iter()
.find(|member| member.0 == circuit_member);
match matched_member {
Some(mut member) => member.1 = return_value.to_owned(),
None => {
return Err(StatementError::UndefinedCircuitObject(
circuit_member.to_string(),
))
}
}
// Modify the range of values of the array in place
match (self.get_mutable_variable(name)?, new_value) {
(ConstrainedValue::Array(old), ConstrainedValue::Array(ref new)) => {
let to_index = to_index_option.unwrap_or(old.len());
old.splice(from_index..to_index, new.iter().cloned());
}
_ => return Err(StatementError::UndefinedCircuit(expected_circuit_name)),
_ => return Err(StatementError::ArrayAssignRange),
}
}
}
@ -123,6 +74,46 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
Ok(())
}
fn mutute_circuit_object(
&mut self,
circuit_name: String,
object_name: Identifier<F, G>,
new_value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
match self.get_mutable_variable(circuit_name)? {
ConstrainedValue::CircuitExpression(_variable, objects) => {
// Modify the circuit member in place
let matched_object = objects.into_iter().find(|object| object.0 == object_name);
match matched_object {
Some(mut object) => object.1 = new_value.to_owned(),
None => {
return Err(StatementError::UndefinedCircuitObject(
object_name.to_string(),
))
}
}
}
_ => return Err(StatementError::UndefinedCircuit(object_name.to_string())),
}
Ok(())
}
fn get_mutable_variable(
&mut self,
name: String,
) -> Result<&mut ConstrainedValue<F, G>, 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::ImmutableAssign(name)),
},
None => return Err(StatementError::UndefinedVariable(name)),
})
}
fn enforce_assign_statement(
&mut self,
cs: &mut CS,
@ -131,21 +122,33 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
assignee: Assignee<F, G>,
expression: Expression<F, G>,
) -> Result<(), StatementError> {
// Check that assignee exists
let name = self.resolve_assignee(function_scope.clone(), assignee.clone());
// Get the name of the variable we are assigning to
let variable_name = self.resolve_assignee(function_scope.clone(), assignee.clone());
match self.get(&name) {
Some(_assignee) => {
let result_value = &mut self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
)?;
// Evaluate new value
let new_value =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), expression)?;
self.store_assignment(cs, file_scope, function_scope, assignee, result_value)
// Mutate the old value into the new value
match assignee {
Assignee::Identifier(_identifier) => {
let old_value = self.get_mutable_variable(variable_name.clone())?;
*old_value = new_value;
Ok(())
}
Assignee::Array(_assignee, range_or_expression) => self.mutate_array(
cs,
file_scope,
function_scope,
variable_name,
range_or_expression,
new_value,
),
Assignee::CircuitMember(_assignee, object_name) => {
self.mutute_circuit_object(variable_name, object_name, new_value)
}
None => Err(StatementError::UndefinedVariable(assignee.to_string())),
}
}

View File

@ -13,7 +13,7 @@ use snarkos_models::{
use std::fmt;
#[derive(Clone, PartialEq, Eq)]
pub struct ConstrainedCircuitMember<F: Field + PrimeField, G: Group>(
pub struct ConstrainedCircuitObject<F: Field + PrimeField, G: Group>(
pub Identifier<F, G>,
pub ConstrainedValue<F, G>,
);
@ -26,7 +26,7 @@ pub enum ConstrainedValue<F: Field + PrimeField, G: Group> {
Boolean(Boolean),
Array(Vec<ConstrainedValue<F, G>>),
CircuitDefinition(Circuit<F, G>),
CircuitExpression(Identifier<F, G>, Vec<ConstrainedCircuitMember<F, G>>),
CircuitExpression(Identifier<F, G>, Vec<ConstrainedCircuitObject<F, G>>),
Function(Function<F, G>),
Return(Vec<ConstrainedValue<F, G>>),
Mutable(Box<ConstrainedValue<F, G>>),

View File

@ -38,6 +38,9 @@ pub enum StatementError {
UndefinedCircuitObject(String),
// Statements
#[error("Cannot assign to immutable variable {}", _0)]
ImmutableAssign(String),
#[error(
"Multiple definition statement expected {} return values, got {}",
_0,

View File

@ -14,7 +14,7 @@ use snarkos_models::gadgets::{
use std::collections::HashMap;
use std::marker::PhantomData;
/// A variable in a constraint system.
/// An identifier in the constrained program.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Identifier<F: Field + PrimeField, G: Group> {
pub name: String,
@ -32,6 +32,7 @@ impl<F: Field + PrimeField, G: Group> Identifier<F, G> {
}
}
/// A variable that is assigned to a value in the constrained program
#[derive(Clone, PartialEq, Eq)]
pub struct Variable<F: Field + PrimeField, G: Group> {
pub identifier: Identifier<F, G>,
@ -153,7 +154,7 @@ pub enum Expression<F: Field + PrimeField, G: Group> {
// Circuits
Circuit(Identifier<F, G>, Vec<CircuitMember<F, G>>),
CircuitMemberAccess(Box<Expression<F, G>>, Identifier<F, G>), // (circuit name, circuit member name)
CircuitMemberAccess(Box<Expression<F, G>>, Identifier<F, G>), // (circuit name, circuit object name)
// Functions
FunctionCall(Identifier<F, G>, Vec<Expression<F, G>>),
@ -164,7 +165,7 @@ pub enum Expression<F: Field + PrimeField, G: Group> {
pub enum Assignee<F: Field + PrimeField, G: Group> {
Identifier(Identifier<F, G>),
Array(Box<Assignee<F, G>>, RangeOrExpression<F, G>),
CircuitMember(Box<Assignee<F, G>>, Identifier<F, G>),
CircuitMember(Box<Assignee<F, G>>, Identifier<F, G>), // (circuit name, circuit object name)
}
/// Explicit integer type