diff --git a/compiler/src/statement/assign/assignee/array_index.rs b/compiler/src/statement/assign/assignee/array_index.rs index ecf6796486..50f58a6277 100644 --- a/compiler/src/statement/assign/assignee/array_index.rs +++ b/compiler/src/statement/assign/assignee/array_index.rs @@ -19,7 +19,7 @@ use std::convert::TryInto; use crate::{ - errors::{ExpressionError, StatementError}, + errors::{ExpressionError, IntegerError, StatementError}, program::ConstrainedProgram, value::ConstrainedValue, GroupType, @@ -40,85 +40,117 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { mut context: ResolverContext<'a, 'b, F, G>, index: &'a Expression<'a>, ) -> Result<(), StatementError> { - if context.input.len() != 1 { + println!("RTAAI input len {}", context.input.len()); + let input_len = context.input.len(); + if input_len == 0 { 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) + match (context.input.remove(0), input_len) { + (ConstrainedValue::Array(input), 1) => { + 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 { - context.input = vec![target]; - self.resolve_target_access(cs, context) + 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, 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 mut unique_namespace = cs.ns(|| { + format!( + "select array dyn assignment {} {}:{}", + i, span.line_start, span.col_start + ) + }); + let temp_item = { + let mut item = item.clone(); + let mut new_context = ResolverContext { + input: vec![&mut item], + span: context.span.clone(), + target_value: context.target_value.clone(), + remaining_accesses: context.remaining_accesses, + indicator: context.indicator, + operation: context.operation, + }; + if context.remaining_accesses.is_empty() { + let item = new_context.input.remove(0); + self.enforce_assign_context(&mut unique_namespace, &new_context, item)?; + } else { + self.resolve_target_access(&mut unique_namespace, new_context)?; + } + item + }; + let value = ConstrainedValue::conditionally_select( + unique_namespace, + &index_comparison, + &temp_item, + &item, + ) + .map_err(|e| ExpressionError::cannot_enforce("conditional select".to_string(), e, &span))?; + *item = value; + } + Ok(()) } } - } 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, 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 mut unique_namespace = cs.ns(|| { - format!( - "select array dyn assignment {} {}:{}", - i, span.line_start, span.col_start - ) - }); - let temp_item = { - let mut item = item.clone(); - let mut new_context = ResolverContext { - input: vec![&mut item], - span: context.span.clone(), - target_value: context.target_value.clone(), - remaining_accesses: context.remaining_accesses, - indicator: context.indicator, - operation: context.operation, - }; - if context.remaining_accesses.is_empty() { - let item = new_context.input.remove(0); - self.enforce_assign_context(&mut unique_namespace, &new_context, item)?; - } else { - self.resolve_target_access(&mut unique_namespace, new_context)?; + (target, _) => { + // index of array range + if let Some(index) = index_resolved.to_usize() { + if index >= input_len { + return Err(StatementError::array_assign_index_bounds( + index, + input_len, + &context.span, + )); } - item - }; - let value = - ConstrainedValue::conditionally_select(unique_namespace, &index_comparison, &temp_item, &item) - .map_err(|e| ExpressionError::cannot_enforce("conditional select".to_string(), e, &span))?; - *item = value; + 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 { + let span = index.span().cloned().unwrap_or_default(); + + Err(StatementError::from(IntegerError::invalid_integer( + index_resolved.to_string(), + &span, + ))) + } } - Ok(()) } } } diff --git a/compiler/src/statement/assign/assignee/mod.rs b/compiler/src/statement/assign/assignee/mod.rs index ca81ccc4ab..230899eb76 100644 --- a/compiler/src/statement/assign/assignee/mod.rs +++ b/compiler/src/statement/assign/assignee/mod.rs @@ -68,8 +68,10 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { self.enforce_assign_context(cs, &context, input)?; return Ok(()); } + let access = context.remaining_accesses[context.remaining_accesses.len() - 1]; context.remaining_accesses = &context.remaining_accesses[..context.remaining_accesses.len() - 1]; + match access { AssignAccess::ArrayRange(start, stop) => { self.resolve_target_access_array_range(cs, context, start.get(), stop.get()) diff --git a/leo/main.rs b/leo/main.rs index 7d92f4f087..2629c52202 100644 --- a/leo/main.rs +++ b/leo/main.rs @@ -308,7 +308,7 @@ mod cli_tests { let path = Some(dir.path("new")); assert!(run_cmd("leo new test", &path).is_ok()); - assert!(run_cmd("leo new test", &path).is_err()); // 2nd time + assert!(run_cmd("leo new test", &path).is_err()); // 2nd time assert!(run_cmd("leo new wrong_name", &path).is_err()); }