From a0acc915e95aaeab03d05852323855869cb2220e Mon Sep 17 00:00:00 2001 From: Protryon Date: Fri, 4 Jun 2021 05:52:58 -0700 Subject: [PATCH 1/6] dynamic array index assignment --- ast/src/statements/assign/mod.rs | 2 +- compiler/src/function/function.rs | 9 +- compiler/src/function/mod.rs | 3 +- compiler/src/function/mut_target.rs | 102 +++---- compiler/src/statement/assign/assign.rs | 45 +-- compiler/src/statement/assign/assignee.rs | 190 ------------- .../statement/assign/assignee/array_index.rs | 101 +++++++ .../assign/assignee/array_range_index.rs | 107 ++++++++ .../src/statement/assign/assignee/member.rs | 58 ++++ compiler/src/statement/assign/assignee/mod.rs | 258 ++++++++++++++++++ .../src/statement/assign/assignee/tuple.rs | 50 ++++ compiler/src/statement/assign/mod.rs | 3 +- test-framework/src/runner.rs | 10 + tests/compiler/mutability/array_dyn_mut.leo | 15 + .../mutability/array_dyn_mut_indirect.leo | 15 + tests/compiler/mutability/input/index1.in | 5 + .../compiler/mutability/input/index1_tuple.in | 5 + tests/compiler/mutability/input/index2.in | 5 + .../compiler/mutability/input/index2_tuple.in | 5 + .../compiler/mutability/array_dyn_mut.leo.out | 24 ++ .../mutability/array_dyn_mut_indirect.leo.out | 24 ++ 21 files changed, 730 insertions(+), 306 deletions(-) delete mode 100644 compiler/src/statement/assign/assignee.rs create mode 100644 compiler/src/statement/assign/assignee/array_index.rs create mode 100644 compiler/src/statement/assign/assignee/array_range_index.rs create mode 100644 compiler/src/statement/assign/assignee/member.rs create mode 100644 compiler/src/statement/assign/assignee/mod.rs create mode 100644 compiler/src/statement/assign/assignee/tuple.rs create mode 100644 tests/compiler/mutability/array_dyn_mut.leo create mode 100644 tests/compiler/mutability/array_dyn_mut_indirect.leo create mode 100644 tests/compiler/mutability/input/index1.in create mode 100644 tests/compiler/mutability/input/index1_tuple.in create mode 100644 tests/compiler/mutability/input/index2.in create mode 100644 tests/compiler/mutability/input/index2_tuple.in create mode 100644 tests/expectations/compiler/compiler/mutability/array_dyn_mut.leo.out create mode 100644 tests/expectations/compiler/compiler/mutability/array_dyn_mut_indirect.leo.out diff --git a/ast/src/statements/assign/mod.rs b/ast/src/statements/assign/mod.rs index ae88fda809..29da40821b 100644 --- a/ast/src/statements/assign/mod.rs +++ b/ast/src/statements/assign/mod.rs @@ -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, diff --git a/compiler/src/function/function.rs b/compiler/src/function/function.rs index 165f8e2135..09a848a7c3 100644 --- a/compiler/src/function/function.rs +++ b/compiler/src/function/function.rs @@ -81,13 +81,8 @@ impl<'a, F: PrimeField, G: GroupType> 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()`) } } diff --git a/compiler/src/function/mod.rs b/compiler/src/function/mod.rs index 2095ebdbf7..19fa978e6c 100644 --- a/compiler/src/function/mod.rs +++ b/compiler/src/function/mod.rs @@ -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; diff --git a/compiler/src/function/mut_target.rs b/compiler/src/function/mut_target.rs index 948873ba01..945cdd4fa5 100644 --- a/compiler/src/function/mut_target.rs +++ b/compiler/src/function/mut_target.rs @@ -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> ConstrainedProgram<'a, F, G> { - fn prepare_mut_access>( - &mut self, - cs: &mut CS, + fn prepare_mut_access( + out: &mut Vec>, expr: &'a Expression<'a>, - span: &Span, - output: &mut Vec, - ) -> Result>, StatementError> { + ) -> Result>, 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()?; - - output.push(ResolvedAssigneeAccess::ArrayRange(start_index, stop_index)); + let inner = Self::prepare_mut_access(out, array.get())?; + + out.push(AssignAccess::ArrayRange(left.clone(), right.clone())); + 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>( &mut self, cs: &mut CS, assignee: &'a Expression<'a>, - ) -> Result>>, StatementError> { - let span = &assignee.span().cloned().unwrap_or_default(); - + target_value: ConstrainedValue<'a, F, G>, + indicator: &Boolean, + ) -> Result { 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) } } diff --git a/compiler/src/statement/assign/assign.rs b/compiler/src/statement/assign/assign.rs index 397da041ce..0221d0feaf 100644 --- a/compiler/src/statement/assign/assign.rs +++ b/compiler/src/statement/assign/assign.rs @@ -33,54 +33,13 @@ impl<'a, F: PrimeField, G: GroupType> 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>( + pub(super) fn enforce_assign_operation>( cs: &mut CS, condition: &Boolean, scope: String, diff --git a/compiler/src/statement/assign/assignee.rs b/compiler/src/statement/assign/assignee.rs deleted file mode 100644 index 8baa0ff0a2..0000000000 --- a/compiler/src/statement/assign/assignee.rs +++ /dev/null @@ -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 . - -//! 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, Option), - ArrayIndex(usize), - Tuple(usize, Span), - Member(Identifier), -} - -impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { - pub fn resolve_assign>( - &mut self, - cs: &mut CS, - assignee: &AssignStatement<'a>, - ) -> Result>, 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::, 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>, 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)), - } - } - } - } -} diff --git a/compiler/src/statement/assign/assignee/array_index.rs b/compiler/src/statement/assign/assignee/array_index.rs new file mode 100644 index 0000000000..5f6485c885 --- /dev/null +++ b/compiler/src/statement/assign/assignee/array_index.rs @@ -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 . + +//! 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> ConstrainedProgram<'a, F, G> { + pub(super) fn resolve_target_access_array_index<'b, CS: ConstraintSystem>( + &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(()) + } + } +} diff --git a/compiler/src/statement/assign/assignee/array_range_index.rs b/compiler/src/statement/assign/assignee/array_range_index.rs new file mode 100644 index 0000000000..505c7164ae --- /dev/null +++ b/compiler/src/statement/assign/assignee/array_range_index.rs @@ -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 . + +//! 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> ConstrainedProgram<'a, F, G> { + pub(super) fn resolve_target_access_array_range<'b, CS: ConstraintSystem>( + &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) + } + } + } +} diff --git a/compiler/src/statement/assign/assignee/member.rs b/compiler/src/statement/assign/assignee/member.rs new file mode 100644 index 0000000000..c51c70514d --- /dev/null +++ b/compiler/src/statement/assign/assignee/member.rs @@ -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 . + +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> ConstrainedProgram<'a, F, G> { + pub(super) fn resolve_target_access_member<'b, CS: ConstraintSystem>( + &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)), + } + } +} diff --git a/compiler/src/statement/assign/assignee/mod.rs b/compiler/src/statement/assign/assignee/mod.rs new file mode 100644 index 0000000000..6a7273ee65 --- /dev/null +++ b/compiler/src/statement/assign/assignee/mod.rs @@ -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 . + +//! 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> { + 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> { +// Value(&'b mut ConstrainedValue<'a, F, G>), +// Ref(u32), +// } + +// impl<'a, 'b, F: PrimeField, G: GroupType> 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> ConstrainedProgram<'a, F, G> { + fn enforce_assign_context<'b, CS: ConstraintSystem>( + &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>( + &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>( + &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>( + // cs: &mut CS, + // access: ResolvedAssigneeAccess, + // span: &Span, + // mut value: Vec<&'b mut ConstrainedValue<'a, F, G>>, + // ) -> Result>, 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)), + // } + // } + // } + // } +} diff --git a/compiler/src/statement/assign/assignee/tuple.rs b/compiler/src/statement/assign/assignee/tuple.rs new file mode 100644 index 0000000000..34df5a3477 --- /dev/null +++ b/compiler/src/statement/assign/assignee/tuple.rs @@ -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 . + +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> ConstrainedProgram<'a, F, G> { + pub(super) fn resolve_target_access_tuple<'b, CS: ConstraintSystem>( + &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)), + } + } +} diff --git a/compiler/src/statement/assign/mod.rs b/compiler/src/statement/assign/mod.rs index 08fc4f2311..ece4dab456 100644 --- a/compiler/src/statement/assign/mod.rs +++ b/compiler/src/statement/assign/mod.rs @@ -19,5 +19,4 @@ pub mod assign; pub use self::assign::*; -pub mod assignee; -pub(crate) use self::assignee::*; +mod assignee; diff --git a/test-framework/src/runner.rs b/test-framework/src/runner.rs index 19143c8eb1..26fa0a59e7 100644 --- a/test-framework/src/runner.rs +++ b/test-framework/src/runner.rs @@ -60,9 +60,18 @@ pub fn run_tests(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(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(), diff --git a/tests/compiler/mutability/array_dyn_mut.leo b/tests/compiler/mutability/array_dyn_mut.leo new file mode 100644 index 0000000000..072f310757 --- /dev/null +++ b/tests/compiler/mutability/array_dyn_mut.leo @@ -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; +} diff --git a/tests/compiler/mutability/array_dyn_mut_indirect.leo b/tests/compiler/mutability/array_dyn_mut_indirect.leo new file mode 100644 index 0000000000..21292550c9 --- /dev/null +++ b/tests/compiler/mutability/array_dyn_mut_indirect.leo @@ -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; +} diff --git a/tests/compiler/mutability/input/index1.in b/tests/compiler/mutability/input/index1.in new file mode 100644 index 0000000000..bc1f7a8b14 --- /dev/null +++ b/tests/compiler/mutability/input/index1.in @@ -0,0 +1,5 @@ +[main] +i: u32 = 1; + +[registers] +r0: [u32; 3] = [0u32; 3]; diff --git a/tests/compiler/mutability/input/index1_tuple.in b/tests/compiler/mutability/input/index1_tuple.in new file mode 100644 index 0000000000..8387302944 --- /dev/null +++ b/tests/compiler/mutability/input/index1_tuple.in @@ -0,0 +1,5 @@ +[main] +i: u32 = 1; + +[registers] +r0: [(u32, u32); 3] = [(0u32, 0u32); 3]; diff --git a/tests/compiler/mutability/input/index2.in b/tests/compiler/mutability/input/index2.in new file mode 100644 index 0000000000..aa0ddad38e --- /dev/null +++ b/tests/compiler/mutability/input/index2.in @@ -0,0 +1,5 @@ +[main] +i: u32 = 2; + +[registers] +r0: [u32; 3] = [0u32; 3]; diff --git a/tests/compiler/mutability/input/index2_tuple.in b/tests/compiler/mutability/input/index2_tuple.in new file mode 100644 index 0000000000..5203b3c81e --- /dev/null +++ b/tests/compiler/mutability/input/index2_tuple.in @@ -0,0 +1,5 @@ +[main] +i: u32 = 2; + +[registers] +r0: [(u32, u32); 3] = [(0u32, 0u32); 3]; diff --git a/tests/expectations/compiler/compiler/mutability/array_dyn_mut.leo.out b/tests/expectations/compiler/compiler/mutability/array_dyn_mut.leo.out new file mode 100644 index 0000000000..b766490e0c --- /dev/null +++ b/tests/expectations/compiler/compiler/mutability/array_dyn_mut.leo.out @@ -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]" diff --git a/tests/expectations/compiler/compiler/mutability/array_dyn_mut_indirect.leo.out b/tests/expectations/compiler/compiler/mutability/array_dyn_mut_indirect.leo.out new file mode 100644 index 0000000000..7c7f624238 --- /dev/null +++ b/tests/expectations/compiler/compiler/mutability/array_dyn_mut_indirect.leo.out @@ -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)]" From f8c4139c3aae4652eb8a123b6e6fba4c5ac8c760 Mon Sep 17 00:00:00 2001 From: Protryon Date: Fri, 4 Jun 2021 05:54:41 -0700 Subject: [PATCH 2/6] misc cleanup --- compiler/src/statement/assign/assignee/mod.rs | 132 ------------------ 1 file changed, 132 deletions(-) diff --git a/compiler/src/statement/assign/assignee/mod.rs b/compiler/src/statement/assign/assignee/mod.rs index 6a7273ee65..14ac679bc6 100644 --- a/compiler/src/statement/assign/assignee/mod.rs +++ b/compiler/src/statement/assign/assignee/mod.rs @@ -37,20 +37,6 @@ struct ResolverContext<'a, 'b, F: PrimeField, G: GroupType> { operation: AssignOperation, } -// pub enum ConstrainedValueOrRef<'b, 'a, F: PrimeField, G: GroupType> { -// Value(&'b mut ConstrainedValue<'a, F, G>), -// Ref(u32), -// } - -// impl<'a, 'b, F: PrimeField, G: GroupType> 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> ConstrainedProgram<'a, F, G> { fn enforce_assign_context<'b, CS: ConstraintSystem>( &mut self, @@ -137,122 +123,4 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { } } - // // todo: this can prob have most of its error checking removed - // pub(crate) fn resolve_assignee_access<'b, CS: ConstraintSystem>( - // cs: &mut CS, - // access: ResolvedAssigneeAccess, - // span: &Span, - // mut value: Vec<&'b mut ConstrainedValue<'a, F, G>>, - // ) -> Result>, 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)), - // } - // } - // } - // } } From 936a35ebd26486c24b3f50c6c7fc4b150819bc4a Mon Sep 17 00:00:00 2001 From: Protryon Date: Fri, 4 Jun 2021 06:02:14 -0700 Subject: [PATCH 3/6] fmt --- compiler/src/statement/assign/assignee/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/statement/assign/assignee/mod.rs b/compiler/src/statement/assign/assignee/mod.rs index 14ac679bc6..a1a8adb8d1 100644 --- a/compiler/src/statement/assign/assignee/mod.rs +++ b/compiler/src/statement/assign/assignee/mod.rs @@ -122,5 +122,4 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { Ok(()) } } - } From dc91b07e5cf77fcacb62b602f7f2b9ad144a65da Mon Sep 17 00:00:00 2001 From: Protryon Date: Fri, 4 Jun 2021 06:03:59 -0700 Subject: [PATCH 4/6] clippy --- asg/src/statement/assign.rs | 4 ++-- ast/src/reducer/canonicalization.rs | 2 +- ast/src/reducer/reconstructing_reducer.rs | 2 +- compiler/src/statement/assign/assignee/array_index.rs | 2 +- test-framework/src/runner.rs | 6 ++---- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/asg/src/statement/assign.rs b/asg/src/statement/assign.rs index b3842d3f30..e1f44b8210 100644 --- a/asg/src/statement/assign.rs +++ b/asg/src/statement/assign.rs @@ -213,7 +213,7 @@ impl<'a> FromAst<'a, leo_ast::AssignStatement> for &'a Statement<'a> { let statement = scope.context.alloc_statement(Statement::Assign(AssignStatement { parent: Cell::new(None), span: Some(statement.span.clone()), - operation: statement.operation.clone(), + operation: statement.operation, target_variable: Cell::new(variable), target_accesses, value: Cell::new(value), @@ -231,7 +231,7 @@ impl<'a> FromAst<'a, leo_ast::AssignStatement> for &'a Statement<'a> { impl<'a> Into for &AssignStatement<'a> { fn into(self) -> leo_ast::AssignStatement { leo_ast::AssignStatement { - operation: self.operation.clone(), + operation: self.operation, assignee: leo_ast::Assignee { identifier: self.target_variable.get().borrow().name.clone(), accesses: self diff --git a/ast/src/reducer/canonicalization.rs b/ast/src/reducer/canonicalization.rs index 0ad3ce4e12..218e90a740 100644 --- a/ast/src/reducer/canonicalization.rs +++ b/ast/src/reducer/canonicalization.rs @@ -347,7 +347,7 @@ impl Canonicalizer { Statement::Assign(AssignStatement { assignee, value, - operation: assign.operation.clone(), + operation: assign.operation, span: assign.span.clone(), }) } diff --git a/ast/src/reducer/reconstructing_reducer.rs b/ast/src/reducer/reconstructing_reducer.rs index 6be99ef978..8cf5c30bb7 100644 --- a/ast/src/reducer/reconstructing_reducer.rs +++ b/ast/src/reducer/reconstructing_reducer.rs @@ -325,7 +325,7 @@ pub trait ReconstructingReducer { value: Expression, ) -> Result { Ok(AssignStatement { - operation: assign.operation.clone(), + operation: assign.operation, assignee, value, span: assign.span.clone(), diff --git a/compiler/src/statement/assign/assignee/array_index.rs b/compiler/src/statement/assign/assignee/array_index.rs index 5f6485c885..14cee72017 100644 --- a/compiler/src/statement/assign/assignee/array_index.rs +++ b/compiler/src/statement/assign/assignee/array_index.rs @@ -65,7 +65,7 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { } } } else { - for (i, item) in input.into_iter().enumerate() { + for (i, item) in input.iter_mut().enumerate() { let namespace_string = format!( "evaluate dyn array assignment eq {} {}:{}", i, context.span.line_start, context.span.col_start diff --git a/test-framework/src/runner.rs b/test-framework/src/runner.rs index 26fa0a59e7..79dcdb9447 100644 --- a/test-framework/src/runner.rs +++ b/test-framework/src/runner.rs @@ -67,10 +67,8 @@ pub fn run_tests(runner: &T, expectation_category: &str) { let mut outputs = vec![]; for (path, content) in tests.into_iter() { - if !filter.is_empty() { - if !path.contains(filter) { - continue; - } + if !filter.is_empty() && !path.contains(filter) { + continue; } let config = extract_test_config(&content); if config.is_none() { From e6b7f0fce3ffd7e5f2250019a24e0f6b089a6e00 Mon Sep 17 00:00:00 2001 From: Protryon Date: Fri, 4 Jun 2021 07:35:50 -0700 Subject: [PATCH 5/6] bounds check --- compiler/src/expression/array/access.rs | 43 ++++++++++++------- .../statement/assign/assignee/array_index.rs | 21 ++++++--- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/compiler/src/expression/array/access.rs b/compiler/src/expression/array/access.rs index 58544548b7..bbaf1929e0 100644 --- a/compiler/src/expression/array/access.rs +++ b/compiler/src/expression/array/access.rs @@ -37,6 +37,33 @@ use snarkvm_gadgets::utilities::{ use snarkvm_r1cs::ConstraintSystem; impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { + pub fn array_bounds_check>( + &mut self, + cs: &mut CS, + index_resolved: &Integer, + array_len: u32, + span: &Span, + ) -> Result<(), ExpressionError> { + let bounds_check = evaluate_lt::( + cs, + ConstrainedValue::Integer(index_resolved.clone()), + ConstrainedValue::Integer(Integer::new( + &ConstInt::U32(array_len).cast_to(&index_resolved.get_type()), + )), + span, + )?; + let bounds_check = match bounds_check { + ConstrainedValue::Boolean(b) => b, + _ => unimplemented!("illegal non-Integer returned from lt"), + }; + let namespace_string = format!("evaluate array access bounds {}:{}", span.line_start, span.col_start); + let mut unique_namespace = cs.ns(|| namespace_string); + bounds_check + .enforce_equal(&mut unique_namespace, &Boolean::Constant(true)) + .map_err(|e| ExpressionError::cannot_enforce("array bounds check".to_string(), e, span))?; + Ok(()) + } + #[allow(clippy::too_many_arguments)] pub fn enforce_array_access>( &mut self, @@ -65,21 +92,7 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { .len() .try_into() .map_err(|_| ExpressionError::array_length_out_of_bounds(span))?; - let bounds_check = evaluate_lt::( - cs, - ConstrainedValue::Integer(index_resolved.clone()), - ConstrainedValue::Integer(Integer::new(&ConstInt::U32(array_len))), - span, - )?; - let bounds_check = match bounds_check { - ConstrainedValue::Boolean(b) => b, - _ => unimplemented!("illegal non-Integer returned from lt"), - }; - let namespace_string = format!("evaluate array access bounds {}:{}", span.line_start, span.col_start); - let mut unique_namespace = cs.ns(|| namespace_string); - bounds_check - .enforce_equal(&mut unique_namespace, &Boolean::Constant(true)) - .map_err(|e| ExpressionError::cannot_enforce("array bounds check".to_string(), e, span))?; + self.array_bounds_check(cs, &&index_resolved, array_len, span)?; } let mut current_value = array.pop().unwrap(); diff --git a/compiler/src/statement/assign/assignee/array_index.rs b/compiler/src/statement/assign/assignee/array_index.rs index 14cee72017..be35e5f1ac 100644 --- a/compiler/src/statement/assign/assignee/array_index.rs +++ b/compiler/src/statement/assign/assignee/array_index.rs @@ -25,7 +25,7 @@ use crate::{ GroupType, Integer, }; -use leo_asg::{ConstInt, Expression}; +use leo_asg::{ConstInt, Expression, Node}; use snarkvm_fields::PrimeField; use snarkvm_gadgets::utilities::{eq::EvaluateEqGadget, select::CondSelectGadget}; @@ -65,25 +65,34 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { } } } else { + let span = index.span().cloned().unwrap_or_default(); + { + let array_len: u32 = input + .len() + .try_into() + .map_err(|_| ExpressionError::array_length_out_of_bounds(&span))?; + self.array_bounds_check(cs, &&index_resolved, array_len, &span)?; + } + for (i, item) in input.iter_mut().enumerate() { let namespace_string = format!( "evaluate dyn array assignment eq {} {}:{}", - i, context.span.line_start, context.span.col_start + 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(&context.span))?; + .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(), &context.span))?; + .map_err(|_| ExpressionError::cannot_evaluate("==".to_string(), &span))?; let unique_namespace = cs.ns(|| { format!( "select array dyn assignment {} {}:{}", - i, context.span.line_start, context.span.col_start + i, span.line_start, span.col_start ) }); let value = ConstrainedValue::conditionally_select( @@ -92,7 +101,7 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { &context.target_value, &item, ) - .map_err(|e| ExpressionError::cannot_enforce("conditional select".to_string(), e, &context.span))?; + .map_err(|e| ExpressionError::cannot_enforce("conditional select".to_string(), e, &span))?; *item = value; } Ok(()) From 5cd05d73767bd3c33bd0914304d5fbb108299d6e Mon Sep 17 00:00:00 2001 From: p Date: Fri, 4 Jun 2021 11:42:14 -0700 Subject: [PATCH 6/6] fix bounds check --- compiler/src/statement/assign/assignee/array_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/statement/assign/assignee/array_index.rs b/compiler/src/statement/assign/assignee/array_index.rs index be35e5f1ac..bd68904b53 100644 --- a/compiler/src/statement/assign/assignee/array_index.rs +++ b/compiler/src/statement/assign/assignee/array_index.rs @@ -49,7 +49,7 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { }; let index_resolved = self.enforce_index(cs, index, &context.span)?; if let Some(index) = index_resolved.to_usize() { - if index > input.len() { + if index >= input.len() { Err(StatementError::array_assign_index_bounds( index, input.len(),