mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-09-11 05:45:30 +03:00
impl mutable circuit and arrays
This commit is contained in:
parent
3b263d7cc9
commit
9f1803f6da
@ -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
|
||||
}
|
@ -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(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>>),
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user