dynamic array index assignment

This commit is contained in:
Protryon 2021-06-04 05:52:58 -07:00
parent c67ef6a3b5
commit a0acc915e9
21 changed files with 729 additions and 305 deletions

View File

@ -22,7 +22,7 @@ use std::fmt;
mod assignee;
pub use assignee::*;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum AssignOperation {
Assign,
Add,

View File

@ -81,13 +81,8 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
.get(self_var.borrow().id)
.expect("no self variable found in mut self context")
.clone();
if let Some(assignable_target) = self.resolve_mut_ref(cs, target)? {
if assignable_target.len() != 1 {
panic!("found tuple as a self assignment target");
}
let assignable_target = assignable_target.into_iter().next().unwrap();
*assignable_target = new_self;
} else {
if !self.resolve_mut_ref(cs, target, new_self, &indicator)? {
// todo: we should report a warning for calling a mutable function on an effectively copied self (i.e. wasn't assignable `tempStruct {x: 5}.myMutSelfFunction()`)
}
}

View File

@ -28,5 +28,4 @@ pub use self::main_function::*;
pub mod result;
pub use self::result::*;
pub mod mut_target;
pub use self::mut_target::*;
mod mut_target;

View File

@ -16,116 +16,96 @@
//! Resolves assignees in a compiled Leo program.
use crate::{
errors::StatementError,
program::ConstrainedProgram,
value::ConstrainedValue,
GroupType,
ResolvedAssigneeAccess,
};
use std::cell::Cell;
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_asg::{
ArrayAccessExpression,
ArrayRangeAccessExpression,
AssignAccess,
AssignOperation,
AssignStatement,
CircuitAccessExpression,
Expression,
Node,
Span,
TupleAccessExpression,
Variable,
};
use snarkvm_fields::PrimeField;
use snarkvm_gadgets::utilities::boolean::Boolean;
use snarkvm_r1cs::ConstraintSystem;
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
fn prepare_mut_access<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
fn prepare_mut_access(
out: &mut Vec<AssignAccess<'a>>,
expr: &'a Expression<'a>,
span: &Span,
output: &mut Vec<ResolvedAssigneeAccess>,
) -> Result<Option<Variable<'a>>, StatementError> {
) -> Result<Option<&'a Variable<'a>>, StatementError> {
match expr {
Expression::ArrayRangeAccess(ArrayRangeAccessExpression { array, left, right, .. }) => {
let inner = self.prepare_mut_access(cs, array.get(), span, output)?;
let start_index = left
.get()
.map(|start| self.enforce_index(cs, start, span))
.transpose()?
.map(|x| {
x.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(span))
})
.transpose()?;
let stop_index = right
.get()
.map(|stop| self.enforce_index(cs, stop, span))
.transpose()?
.map(|x| {
x.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(span))
})
.transpose()?;
let inner = Self::prepare_mut_access(out, array.get())?;
out.push(AssignAccess::ArrayRange(left.clone(), right.clone()));
output.push(ResolvedAssigneeAccess::ArrayRange(start_index, stop_index));
Ok(inner)
}
Expression::ArrayAccess(ArrayAccessExpression { array, index, .. }) => {
let inner = self.prepare_mut_access(cs, array.get(), span, output)?;
let index = self
.enforce_index(cs, index.get(), span)?
.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(span))?;
let inner = Self::prepare_mut_access(out, array.get())?;
output.push(ResolvedAssigneeAccess::ArrayIndex(index));
out.push(AssignAccess::ArrayIndex(index.clone()));
Ok(inner)
}
Expression::TupleAccess(TupleAccessExpression { tuple_ref, index, .. }) => {
let inner = self.prepare_mut_access(cs, tuple_ref.get(), span, output)?;
let inner = Self::prepare_mut_access(out, tuple_ref.get())?;
output.push(ResolvedAssigneeAccess::Tuple(*index, span.clone()));
out.push(AssignAccess::Tuple(*index));
Ok(inner)
}
Expression::CircuitAccess(CircuitAccessExpression { target, member, .. }) => {
if let Some(target) = target.get() {
let inner = self.prepare_mut_access(cs, target, span, output)?;
let inner = Self::prepare_mut_access(out, target)?;
output.push(ResolvedAssigneeAccess::Member(member.clone()));
out.push(AssignAccess::Member(member.clone()));
Ok(inner)
} else {
Ok(None)
}
}
Expression::VariableRef(variable_ref) => Ok(Some(variable_ref.variable.clone())),
Expression::VariableRef(variable_ref) => Ok(Some(variable_ref.variable)),
_ => Ok(None), // not a valid reference to mutable variable, we copy
}
}
// resolve a mutable reference from an expression
// return Ok(None) if no valid mutable reference, or Err(_) on more critical error
// return false if no valid mutable reference, or Err(_) on more critical error
pub fn resolve_mut_ref<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
assignee: &'a Expression<'a>,
) -> Result<Option<Vec<&mut ConstrainedValue<'a, F, G>>>, StatementError> {
let span = &assignee.span().cloned().unwrap_or_default();
target_value: ConstrainedValue<'a, F, G>,
indicator: &Boolean,
) -> Result<bool, StatementError> {
let mut accesses = vec![];
let target = self.prepare_mut_access(cs, assignee, span, &mut accesses)?;
let target = Self::prepare_mut_access(&mut accesses, assignee)?;
if target.is_none() {
return Ok(None);
return Ok(false);
}
let variable = target.unwrap();
let variable = variable.borrow();
let mut result = vec![match self.get_mut(variable.id) {
Some(value) => value,
None => return Err(StatementError::undefined_variable(variable.name.to_string(), span)),
}];
self.resolve_assign(
cs,
&AssignStatement {
parent: Cell::new(None),
span: assignee.span().cloned(),
operation: AssignOperation::Assign,
target_variable: Cell::new(variable),
target_accesses: accesses,
value: Cell::new(assignee),
},
target_value,
indicator,
)?;
for access in accesses {
result = Self::resolve_assignee_access(access, span, result)?;
}
Ok(Some(result))
Ok(true)
}
}

View File

@ -33,54 +33,13 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
) -> Result<(), StatementError> {
// Get the name of the variable we are assigning to
let new_value = self.enforce_expression(cs, statement.value.get())?;
let mut resolved_assignee = self.resolve_assign(cs, statement)?;
if resolved_assignee.len() == 1 {
let span = statement.span.clone().unwrap_or_default();
Self::enforce_assign_operation(
cs,
indicator,
format!("select {} {}:{}", new_value, &span.line_start, &span.col_start),
&statement.operation,
resolved_assignee[0],
new_value,
&span,
)?;
} else {
match new_value {
ConstrainedValue::Array(new_values) => {
let span = statement.span.clone().unwrap_or_default();
for (i, (old_ref, new_value)) in
resolved_assignee.into_iter().zip(new_values.into_iter()).enumerate()
{
Self::enforce_assign_operation(
cs,
indicator,
format!(
"select-splice {} {} {}:{}",
i, new_value, &span.line_start, &span.col_start
),
&statement.operation,
old_ref,
new_value,
&span,
)?;
}
}
_ => {
return Err(StatementError::array_assign_range(
&statement.span.clone().unwrap_or_default(),
));
}
};
}
self.resolve_assign(cs, statement, new_value, indicator)?;
Ok(())
}
fn enforce_assign_operation<CS: ConstraintSystem<F>>(
pub(super) fn enforce_assign_operation<CS: ConstraintSystem<F>>(
cs: &mut CS,
condition: &Boolean,
scope: String,

View File

@ -1,190 +0,0 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! Resolves assignees in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_asg::{AssignAccess, AssignStatement, Identifier, Span};
use snarkvm_fields::PrimeField;
use snarkvm_r1cs::ConstraintSystem;
pub(crate) enum ResolvedAssigneeAccess {
ArrayRange(Option<usize>, Option<usize>),
ArrayIndex(usize),
Tuple(usize, Span),
Member(Identifier),
}
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
pub fn resolve_assign<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
assignee: &AssignStatement<'a>,
) -> Result<Vec<&mut ConstrainedValue<'a, F, G>>, StatementError> {
let span = assignee.span.clone().unwrap_or_default();
let resolved_accesses = assignee
.target_accesses
.iter()
.map(|access| match access {
AssignAccess::ArrayRange(start, stop) => {
let start_index = start
.get()
.map(|start| self.enforce_index(cs, start, &span))
.transpose()?
.map(|x| {
x.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(&span))
})
.transpose()?;
let stop_index = stop
.get()
.map(|stop| self.enforce_index(cs, stop, &span))
.transpose()?
.map(|x| {
x.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(&span))
})
.transpose()?;
Ok(ResolvedAssigneeAccess::ArrayRange(start_index, stop_index))
}
AssignAccess::ArrayIndex(index) => {
let index = self
.enforce_index(cs, index.get(), &span)?
.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(&span))?;
Ok(ResolvedAssigneeAccess::ArrayIndex(index))
}
AssignAccess::Tuple(index) => Ok(ResolvedAssigneeAccess::Tuple(*index, span.clone())),
AssignAccess::Member(identifier) => Ok(ResolvedAssigneeAccess::Member(identifier.clone())),
})
.collect::<Result<Vec<_>, StatementError>>()?;
let variable = assignee.target_variable.get().borrow();
let mut result = vec![match self.get_mut(variable.id) {
Some(value) => value,
None => return Err(StatementError::undefined_variable(variable.name.to_string(), &span)),
}];
for access in resolved_accesses {
result = Self::resolve_assignee_access(access, &span, result)?;
}
Ok(result)
}
fn check_range_index(start_index: usize, stop_index: usize, len: usize, span: &Span) -> Result<(), StatementError> {
if stop_index < start_index {
Err(StatementError::array_assign_range_order(
start_index,
stop_index,
len,
span,
))
} else if start_index > len {
Err(StatementError::array_assign_index_bounds(start_index, len, span))
} else if stop_index > len {
Err(StatementError::array_assign_index_bounds(stop_index, len, span))
} else {
Ok(())
}
}
// todo: this can prob have most of its error checking removed
pub(crate) fn resolve_assignee_access<'b>(
access: ResolvedAssigneeAccess,
span: &Span,
mut value: Vec<&'b mut ConstrainedValue<'a, F, G>>,
) -> Result<Vec<&'b mut ConstrainedValue<'a, F, G>>, StatementError> {
match access {
ResolvedAssigneeAccess::ArrayIndex(index) => {
if value.len() != 1 {
return Err(StatementError::array_assign_interior_index(span));
}
match value.remove(0) {
ConstrainedValue::Array(old) => {
if index > old.len() {
Err(StatementError::array_assign_index_bounds(index, old.len(), span))
} else {
Ok(vec![old.get_mut(index).unwrap()])
}
}
_ => Err(StatementError::array_assign_index(span)),
}
}
ResolvedAssigneeAccess::ArrayRange(start_index, stop_index) => {
let start_index = start_index.unwrap_or(0);
if value.len() == 1 {
// not a range of a range
match value.remove(0) {
ConstrainedValue::Array(old) => {
let stop_index = stop_index.unwrap_or(old.len());
Self::check_range_index(start_index, stop_index, old.len(), span)?;
Ok(old[start_index..stop_index].iter_mut().collect())
}
_ => Err(StatementError::array_assign_index(span)),
}
} else {
// range of a range
let stop_index = stop_index.unwrap_or(value.len());
Self::check_range_index(start_index, stop_index, value.len(), span)?;
Ok(value.drain(start_index..stop_index).collect())
}
}
ResolvedAssigneeAccess::Tuple(index, span) => {
if value.len() != 1 {
return Err(StatementError::array_assign_interior_index(&span));
}
match value.remove(0) {
ConstrainedValue::Tuple(old) => {
if index > old.len() {
Err(StatementError::tuple_assign_index_bounds(index, old.len(), &span))
} else {
Ok(vec![&mut old[index]])
}
}
_ => Err(StatementError::tuple_assign_index(&span)),
}
}
ResolvedAssigneeAccess::Member(name) => {
if value.len() != 1 {
return Err(StatementError::array_assign_interior_index(span));
}
match value.remove(0) {
ConstrainedValue::CircuitExpression(_variable, members) => {
// Modify the circuit variable in place
let matched_variable = members.iter_mut().find(|member| member.0 == name);
match matched_variable {
Some(member) => Ok(vec![&mut member.1]),
None => {
// Throw an error if the circuit variable does not exist in the circuit
Err(StatementError::undefined_circuit_variable(name.to_string(), span))
}
}
}
// Throw an error if the circuit definition does not exist in the file
x => Err(StatementError::undefined_circuit(x.to_string(), span)),
}
}
}
}
}

View File

@ -0,0 +1,101 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! Resolves assignees in a compiled Leo program.
use std::convert::TryInto;
use crate::{
errors::{ExpressionError, StatementError},
program::ConstrainedProgram,
value::ConstrainedValue,
GroupType,
Integer,
};
use leo_asg::{ConstInt, Expression};
use snarkvm_fields::PrimeField;
use snarkvm_gadgets::utilities::{eq::EvaluateEqGadget, select::CondSelectGadget};
use snarkvm_r1cs::ConstraintSystem;
use super::ResolverContext;
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
pub(super) fn resolve_target_access_array_index<'b, CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
mut context: ResolverContext<'a, 'b, F, G>,
index: &'a Expression<'a>,
) -> Result<(), StatementError> {
if context.input.len() != 1 {
return Err(StatementError::array_assign_interior_index(&context.span));
}
let input = match context.input.remove(0) {
ConstrainedValue::Array(old) => old,
_ => return Err(StatementError::array_assign_index(&context.span)),
};
let index_resolved = self.enforce_index(cs, index, &context.span)?;
if let Some(index) = index_resolved.to_usize() {
if index > input.len() {
Err(StatementError::array_assign_index_bounds(
index,
input.len(),
&context.span,
))
} else {
let target = input.get_mut(index).unwrap();
if context.remaining_accesses.is_empty() {
self.enforce_assign_context(cs, &context, target)
} else {
context.input = vec![target];
self.resolve_target_access(cs, context)
}
}
} else {
for (i, item) in input.into_iter().enumerate() {
let namespace_string = format!(
"evaluate dyn array assignment eq {} {}:{}",
i, context.span.line_start, context.span.col_start
);
let eq_namespace = cs.ns(|| namespace_string);
let index_bounded = i
.try_into()
.map_err(|_| ExpressionError::array_index_out_of_legal_bounds(&context.span))?;
let const_index = ConstInt::U32(index_bounded).cast_to(&index_resolved.get_type());
let index_comparison = index_resolved
.evaluate_equal(eq_namespace, &Integer::new(&const_index))
.map_err(|_| ExpressionError::cannot_evaluate("==".to_string(), &context.span))?;
let unique_namespace = cs.ns(|| {
format!(
"select array dyn assignment {} {}:{}",
i, context.span.line_start, context.span.col_start
)
});
let value = ConstrainedValue::conditionally_select(
unique_namespace,
&index_comparison,
&context.target_value,
&item,
)
.map_err(|e| ExpressionError::cannot_enforce("conditional select".to_string(), e, &context.span))?;
*item = value;
}
Ok(())
}
}
}

View File

@ -0,0 +1,107 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! Resolves assignees in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_asg::Expression;
use snarkvm_fields::PrimeField;
use snarkvm_r1cs::ConstraintSystem;
use super::ResolverContext;
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
pub(super) fn resolve_target_access_array_range<'b, CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
mut context: ResolverContext<'a, 'b, F, G>,
start: Option<&'a Expression<'a>>,
stop: Option<&'a Expression<'a>>,
) -> Result<(), StatementError> {
let start_index = start
.map(|start| self.enforce_index(cs, start, &context.span))
.transpose()?
.map(|x| {
x.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(&context.span))
})
.transpose()?;
let stop_index = stop
.map(|stop| self.enforce_index(cs, stop, &context.span))
.transpose()?
.map(|x| {
x.to_usize()
.ok_or_else(|| StatementError::array_assign_index_const(&context.span))
})
.transpose()?;
let start_index = start_index.unwrap_or(0);
if context.input.len() == 1 {
// not a range of a range
match context.input.remove(0) {
ConstrainedValue::Array(old) => {
let stop_index = stop_index.unwrap_or(old.len());
Self::check_range_index(start_index, stop_index, old.len(), &context.span)?;
if context.remaining_accesses.is_empty() {
let target_values = match context.target_value {
ConstrainedValue::Array(x) => x,
_ => unimplemented!(),
};
for (target, target_value) in old[start_index..stop_index].iter_mut().zip(target_values) {
context.target_value = target_value;
self.enforce_assign_context(cs, &context, target)?;
}
} else {
context.input = old[start_index..stop_index].iter_mut().collect();
self.resolve_target_access(cs, context)?;
}
Ok(())
}
_ => Err(StatementError::array_assign_index(&context.span)),
}
} else {
// range of a range
let stop_index = stop_index.unwrap_or(context.input.len());
Self::check_range_index(start_index, stop_index, context.input.len(), &context.span)?;
context.input = context
.input
.into_iter()
.skip(start_index)
.take(stop_index - start_index)
.collect();
if context.remaining_accesses.is_empty() {
let target_values = match context.target_value {
ConstrainedValue::Array(x) => x,
_ => unimplemented!(),
};
let iter = context.input.into_iter().zip(target_values.into_iter());
context.input = vec![];
for (target, target_value) in iter {
context.target_value = target_value;
self.enforce_assign_context(cs, &context, target)?;
}
Ok(())
} else {
self.resolve_target_access(cs, context)
}
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_asg::Identifier;
use snarkvm_fields::PrimeField;
use snarkvm_r1cs::ConstraintSystem;
use super::ResolverContext;
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
pub(super) fn resolve_target_access_member<'b, CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
mut context: ResolverContext<'a, 'b, F, G>,
name: &Identifier,
) -> Result<(), StatementError> {
if context.input.len() != 1 {
return Err(StatementError::array_assign_interior_index(&context.span));
}
match context.input.remove(0) {
ConstrainedValue::CircuitExpression(_variable, members) => {
// Modify the circuit variable in place
let matched_variable = members.iter_mut().find(|member| &member.0 == name);
match matched_variable {
Some(member) => {
context.input = vec![&mut member.1];
self.resolve_target_access(cs, context)
}
None => {
// Throw an error if the circuit variable does not exist in the circuit
Err(StatementError::undefined_circuit_variable(
name.to_string(),
&context.span,
))
}
}
}
// Throw an error if the circuit definition does not exist in the file
x => Err(StatementError::undefined_circuit(x.to_string(), &context.span)),
}
}
}

View File

@ -0,0 +1,258 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! Resolves assignees in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_asg::{AssignAccess, AssignOperation, AssignStatement, Span};
use snarkvm_fields::PrimeField;
use snarkvm_gadgets::utilities::boolean::Boolean;
use snarkvm_r1cs::ConstraintSystem;
mod array_index;
mod array_range_index;
mod member;
mod tuple;
struct ResolverContext<'a, 'b, F: PrimeField, G: GroupType<F>> {
input: Vec<&'b mut ConstrainedValue<'a, F, G>>,
span: Span,
target_value: ConstrainedValue<'a, F, G>,
remaining_accesses: Vec<&'b AssignAccess<'a>>,
indicator: &'b Boolean,
operation: AssignOperation,
}
// pub enum ConstrainedValueOrRef<'b, 'a, F: PrimeField, G: GroupType<F>> {
// Value(&'b mut ConstrainedValue<'a, F, G>),
// Ref(u32),
// }
// impl<'a, 'b, F: PrimeField, G: GroupType<F>> ConstrainedValueOrRef<'a, 'b, F, G> {
// pub fn resolve_mut(self, program: &'b mut ConstrainedProgram<'a, F, G>) -> &'b mut ConstrainedValue<'a, F, G> {
// match self {
// ConstrainedValueOrRef::Value(x) => x,
// ConstrainedValueOrRef::Ref(x) => program.get_mut(x).expect("missing var ref"),
// }
// }
// }
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
fn enforce_assign_context<'b, CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
context: &ResolverContext<'a, 'b, F, G>,
target: &mut ConstrainedValue<'a, F, G>,
) -> Result<(), StatementError> {
Self::enforce_assign_operation(
cs,
context.indicator,
format!("select_assign {}:{}", &context.span.line_start, &context.span.col_start),
&context.operation,
target,
context.target_value.clone(),
&context.span,
)
}
fn resolve_target_access<'b, CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
mut context: ResolverContext<'a, 'b, F, G>,
) -> Result<(), StatementError> {
if context.remaining_accesses.is_empty() {
if context.input.len() != 1 {
panic!("invalid non-array-context multi-value assignment");
}
let input = context.input.remove(0);
self.enforce_assign_context(cs, &context, input)?;
return Ok(());
}
match context.remaining_accesses.pop().unwrap() {
AssignAccess::ArrayRange(start, stop) => {
self.resolve_target_access_array_range(cs, context, start.get(), stop.get())
}
AssignAccess::ArrayIndex(index) => self.resolve_target_access_array_index(cs, context, index.get()),
AssignAccess::Tuple(index) => self.resolve_target_access_tuple(cs, context, *index),
AssignAccess::Member(identifier) => self.resolve_target_access_member(cs, context, identifier),
}
}
pub fn resolve_assign<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
assignee: &AssignStatement<'a>,
target_value: ConstrainedValue<'a, F, G>,
indicator: &Boolean,
) -> Result<(), StatementError> {
let span = assignee.span.clone().unwrap_or_default();
let variable = assignee.target_variable.get().borrow();
let mut target = self.get(variable.id).unwrap().clone();
self.resolve_target_access(cs, ResolverContext {
input: vec![&mut target],
span,
target_value,
remaining_accesses: assignee.target_accesses.iter().rev().collect(),
indicator,
operation: assignee.operation,
})?;
*self.get_mut(variable.id).unwrap() = target;
Ok(())
}
pub(crate) fn check_range_index(
start_index: usize,
stop_index: usize,
len: usize,
span: &Span,
) -> Result<(), StatementError> {
if stop_index < start_index {
Err(StatementError::array_assign_range_order(
start_index,
stop_index,
len,
span,
))
} else if start_index > len {
Err(StatementError::array_assign_index_bounds(start_index, len, span))
} else if stop_index > len {
Err(StatementError::array_assign_index_bounds(stop_index, len, span))
} else {
Ok(())
}
}
// // todo: this can prob have most of its error checking removed
// pub(crate) fn resolve_assignee_access<'b, CS: ConstraintSystem<F>>(
// cs: &mut CS,
// access: ResolvedAssigneeAccess,
// span: &Span,
// mut value: Vec<&'b mut ConstrainedValue<'a, F, G>>,
// ) -> Result<Vec<&'b mut ConstrainedValue<'a, F, G>>, StatementError> {
// match access {
// ResolvedAssigneeAccess::ArrayIndex(index) => {
// if value.len() != 1 {
// return Err(StatementError::array_assign_interior_index(span));
// }
// match value.remove(0) {
// ConstrainedValue::Array(old) => {
// if index > old.len() {
// Err(StatementError::array_assign_index_bounds(index, old.len(), span))
// } else {
// Ok(vec![old.get_mut(index).unwrap()])
// }
// }
// _ => Err(StatementError::array_assign_index(span)),
// }
// },
// ResolvedAssigneeAccess::DynArrayIndex(index_resolved) => {
// if value.len() != 1 {
// return Err(StatementError::array_assign_interior_index(span));
// }
// match value.remove(0) {
// ConstrainedValue::Array(old) => {
// for (i, item) in old.into_iter().enumerate() {
// let namespace_string = format!("evaluate dyn array assignment eq {} {}:{}", i, span.line_start, span.col_start);
// let eq_namespace = cs.ns(|| namespace_string);
// let index_bounded = i
// .try_into()
// .map_err(|_| ExpressionError::array_index_out_of_legal_bounds(span))?;
// let const_index = ConstInt::U32(index_bounded).cast_to(&index_resolved.get_type());
// let index_comparison = index_resolved
// .evaluate_equal(eq_namespace, &Integer::new(&const_index))
// .map_err(|_| ExpressionError::cannot_evaluate("==".to_string(), span))?;
// let unique_namespace =
// cs.ns(|| format!("select array dyn assignment {} {}:{}", i, span.line_start, span.col_start));
// let mut_container = ConstrainedValue::Illegal;
// let value =
// ConstrainedValue::conditionally_select(unique_namespace, &index_comparison, &mut_container, &item)
// .map_err(|e| ExpressionError::cannot_enforce("conditional select".to_string(), e, span))?;
// }
// if index > old.len() {
// Err(StatementError::array_assign_index_bounds(index, old.len(), span))
// } else {
// Ok(vec![old.get_mut(index).unwrap()])
// }
// }
// _ => Err(StatementError::array_assign_index(span)),
// }
// },
// ResolvedAssigneeAccess::ArrayRange(start_index, stop_index) => {
// let start_index = start_index.unwrap_or(0);
// if value.len() == 1 {
// // not a range of a range
// match value.remove(0) {
// ConstrainedValue::Array(old) => {
// let stop_index = stop_index.unwrap_or(old.len());
// Self::check_range_index(start_index, stop_index, old.len(), span)?;
// Ok(old[start_index..stop_index].iter_mut().collect())
// }
// _ => Err(StatementError::array_assign_index(span)),
// }
// } else {
// // range of a range
// let stop_index = stop_index.unwrap_or(value.len());
// Self::check_range_index(start_index, stop_index, value.len(), span)?;
// Ok(value.drain(start_index..stop_index).collect())
// }
// }
// ResolvedAssigneeAccess::Tuple(index, span) => {
// if value.len() != 1 {
// return Err(StatementError::array_assign_interior_index(&span));
// }
// match value.remove(0) {
// ConstrainedValue::Tuple(old) => {
// if index > old.len() {
// Err(StatementError::tuple_assign_index_bounds(index, old.len(), &span))
// } else {
// Ok(vec![&mut old[index]])
// }
// }
// _ => Err(StatementError::tuple_assign_index(&span)),
// }
// }
// ResolvedAssigneeAccess::Member(name) => {
// if value.len() != 1 {
// return Err(StatementError::array_assign_interior_index(span));
// }
// match value.remove(0) {
// ConstrainedValue::CircuitExpression(_variable, members) => {
// // Modify the circuit variable in place
// let matched_variable = members.iter_mut().find(|member| member.0 == name);
// match matched_variable {
// Some(member) => Ok(vec![&mut member.1]),
// None => {
// // Throw an error if the circuit variable does not exist in the circuit
// Err(StatementError::undefined_circuit_variable(name.to_string(), span))
// }
// }
// }
// // Throw an error if the circuit definition does not exist in the file
// x => Err(StatementError::undefined_circuit(x.to_string(), span)),
// }
// }
// }
// }
}

View File

@ -0,0 +1,50 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use snarkvm_fields::PrimeField;
use snarkvm_r1cs::ConstraintSystem;
use super::ResolverContext;
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
pub(super) fn resolve_target_access_tuple<'b, CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
mut context: ResolverContext<'a, 'b, F, G>,
index: usize,
) -> Result<(), StatementError> {
if context.input.len() != 1 {
return Err(StatementError::array_assign_interior_index(&context.span));
}
match context.input.remove(0) {
ConstrainedValue::Tuple(old) => {
if index > old.len() {
Err(StatementError::tuple_assign_index_bounds(
index,
old.len(),
&context.span,
))
} else {
context.input = vec![&mut old[index]];
self.resolve_target_access(cs, context)
}
}
_ => Err(StatementError::tuple_assign_index(&context.span)),
}
}
}

View File

@ -19,5 +19,4 @@
pub mod assign;
pub use self::assign::*;
pub mod assignee;
pub(crate) use self::assignee::*;
mod assignee;

View File

@ -60,9 +60,18 @@ pub fn run_tests<T: Runner>(runner: &T, expectation_category: &str) {
expectation_dir.push("expectations");
find_tests(&test_dir, &mut tests);
let filter = std::env::var("TEST_FILTER").unwrap_or_default();
let filter = filter.trim();
let mut outputs = vec![];
for (path, content) in tests.into_iter() {
if !filter.is_empty() {
if !path.contains(filter) {
continue;
}
}
let config = extract_test_config(&content);
if config.is_none() {
//panic!("missing configuration for {}", path);
@ -139,6 +148,7 @@ pub fn run_tests<T: Runner>(runner: &T, expectation_category: &str) {
let mut expected_output = expectations.as_ref().map(|x| x.outputs.iter());
for (i, test) in tests.into_iter().enumerate() {
let expected_output = expected_output.as_mut().map(|x| x.next()).flatten().cloned();
println!("running test {} @ '{}'", test_name, path.to_str().unwrap());
let output = namespace.run_test(Test {
name: test_name.clone(),
content: test.clone(),

View File

@ -0,0 +1,15 @@
/*
namespace: Compile
expectation: Pass
input_file:
- input/index1.in
- input/index2.in
*/
function main(i: u32) -> [u32; 3] {
let a = [1u32, 2u32, 3u32];
a[i - 1] += a[i];
a[i] = 0;
return a;
}

View File

@ -0,0 +1,15 @@
/*
namespace: Compile
expectation: Pass
input_file:
- input/index1_tuple.in
- input/index2_tuple.in
*/
function main(i: u32) -> [(u32, u32); 3] {
let a = [(1u32, 1u32), (2u32, 2u32), (3u32, 3u32)];
a[i].0 = 0;
a[i].1 = 1;
return a;
}

View File

@ -0,0 +1,5 @@
[main]
i: u32 = 1;
[registers]
r0: [u32; 3] = [0u32; 3];

View File

@ -0,0 +1,5 @@
[main]
i: u32 = 1;
[registers]
r0: [(u32, u32); 3] = [(0u32, 0u32); 3];

View File

@ -0,0 +1,5 @@
[main]
i: u32 = 2;
[registers]
r0: [u32; 3] = [0u32; 3];

View File

@ -0,0 +1,5 @@
[main]
i: u32 = 2;
[registers]
r0: [(u32, u32); 3] = [(0u32, 0u32); 3];

View File

@ -0,0 +1,24 @@
---
namespace: Compile
expectation: Pass
outputs:
- circuit:
num_public_variables: 0
num_private_variables: 65
num_constraints: 66
at: 0c8cb242ee3815f5ea6fdd08873577a6cc51fcfbda8cb5671f0b4dc851939d07
bt: 32bbce55a7ed95a5b0277ef5ddfa171ad065c5c7c743031d8b0bd8e50e51b0af
ct: b0a68f3915594800e43cb144dc499a002fd302374532313e835dfa163d38f0f1
output:
- input_file: input/index1.in
output:
registers:
r0:
type: "[u32; 3]"
value: "[3, 0, 3]"
- input_file: input/index2.in
output:
registers:
r0:
type: "[u32; 3]"
value: "[1, 5, 0]"

View File

@ -0,0 +1,24 @@
---
namespace: Compile
expectation: Pass
outputs:
- circuit:
num_public_variables: 0
num_private_variables: 32
num_constraints: 32
at: 4f36fe54f989d60bb9c279120800f4f44596c2efb7ba703669d4c4d591569780
bt: d378030968a64801f66d95699329086ca17e676d8bffcf73f6b431cbda7c7005
ct: dbd098af6556ed79650d149b1691be336a46f8bad6f327e942508dd11342575e
output:
- input_file: input/index1_tuple.in
output:
registers:
r0:
type: "[(u32, u32); 3]"
value: "[(1, 1), (0, 1), (3, 3)]"
- input_file: input/index2_tuple.in
output:
registers:
r0:
type: "[(u32, u32); 3]"
value: "[(1, 1), (2, 2), (0, 1)]"