mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-23 18:21:38 +03:00
dynamic array index assignment
This commit is contained in:
parent
c67ef6a3b5
commit
a0acc915e9
@ -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,
|
||||
|
@ -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()`)
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
compiler/src/statement/assign/assignee/array_index.rs
Normal file
101
compiler/src/statement/assign/assignee/array_index.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
107
compiler/src/statement/assign/assignee/array_range_index.rs
Normal file
107
compiler/src/statement/assign/assignee/array_range_index.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
58
compiler/src/statement/assign/assignee/member.rs
Normal file
58
compiler/src/statement/assign/assignee/member.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
258
compiler/src/statement/assign/assignee/mod.rs
Normal file
258
compiler/src/statement/assign/assignee/mod.rs
Normal 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)),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
50
compiler/src/statement/assign/assignee/tuple.rs
Normal file
50
compiler/src/statement/assign/assignee/tuple.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
@ -19,5 +19,4 @@
|
||||
pub mod assign;
|
||||
pub use self::assign::*;
|
||||
|
||||
pub mod assignee;
|
||||
pub(crate) use self::assignee::*;
|
||||
mod assignee;
|
||||
|
@ -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(),
|
||||
|
15
tests/compiler/mutability/array_dyn_mut.leo
Normal file
15
tests/compiler/mutability/array_dyn_mut.leo
Normal 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;
|
||||
}
|
15
tests/compiler/mutability/array_dyn_mut_indirect.leo
Normal file
15
tests/compiler/mutability/array_dyn_mut_indirect.leo
Normal 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;
|
||||
}
|
5
tests/compiler/mutability/input/index1.in
Normal file
5
tests/compiler/mutability/input/index1.in
Normal file
@ -0,0 +1,5 @@
|
||||
[main]
|
||||
i: u32 = 1;
|
||||
|
||||
[registers]
|
||||
r0: [u32; 3] = [0u32; 3];
|
5
tests/compiler/mutability/input/index1_tuple.in
Normal file
5
tests/compiler/mutability/input/index1_tuple.in
Normal file
@ -0,0 +1,5 @@
|
||||
[main]
|
||||
i: u32 = 1;
|
||||
|
||||
[registers]
|
||||
r0: [(u32, u32); 3] = [(0u32, 0u32); 3];
|
5
tests/compiler/mutability/input/index2.in
Normal file
5
tests/compiler/mutability/input/index2.in
Normal file
@ -0,0 +1,5 @@
|
||||
[main]
|
||||
i: u32 = 2;
|
||||
|
||||
[registers]
|
||||
r0: [u32; 3] = [0u32; 3];
|
5
tests/compiler/mutability/input/index2_tuple.in
Normal file
5
tests/compiler/mutability/input/index2_tuple.in
Normal file
@ -0,0 +1,5 @@
|
||||
[main]
|
||||
i: u32 = 2;
|
||||
|
||||
[registers]
|
||||
r0: [(u32, u32); 3] = [(0u32, 0u32); 3];
|
@ -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]"
|
@ -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)]"
|
Loading…
Reference in New Issue
Block a user