Merge pull request #1074 from AleoHQ/bug/ternery-and-canonicalization-fixes

Bug-Fix: Canonicalization, Array Init, Array Indexing
This commit is contained in:
Alessandro Coglio 2021-06-29 11:12:49 -07:00 committed by GitHub
commit 2e0dab122b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1309 additions and 723 deletions

View File

@ -54,8 +54,8 @@ impl<'a> ExpressionNode<'a> for ArrayInitExpression<'a> {
}
fn const_value(&self) -> Option<ConstValue> {
// not implemented due to performance concerns
None
let element = self.element.get().const_value()?;
Some(ConstValue::Array(vec![element; self.len]))
}
fn is_consty(&self) -> bool {

View File

@ -593,19 +593,13 @@ impl ReconstructingReducer for Canonicalizer {
value: Expression,
) -> Result<AssignStatement, ReducerError> {
match value {
Expression::Binary(binary_expr) if assign.operation == AssignOperation::Assign => Ok(AssignStatement {
operation: AssignOperation::Assign,
assignee,
value: Expression::Binary(binary_expr),
span: assign.span.clone(),
}),
Expression::Binary(binary_expr) if assign.operation != AssignOperation::Assign => {
value if assign.operation != AssignOperation::Assign => {
let left = self.canonicalize_accesses(
Expression::Identifier(assignee.identifier.clone()),
&assignee.accesses,
&assign.span,
)?;
let right = Box::new(Expression::Binary(binary_expr));
let right = Box::new(value);
let op = self.compound_operation_converstion(&assign.operation)?;
let new_value = Expression::Binary(BinaryExpression {
@ -622,36 +616,12 @@ impl ReconstructingReducer for Canonicalizer {
span: assign.span.clone(),
})
}
Expression::Value(value_expr) if assign.operation != AssignOperation::Assign => {
let left = self.canonicalize_accesses(
Expression::Identifier(assignee.identifier.clone()),
&assignee.accesses,
&assign.span,
)?;
let right = Box::new(Expression::Value(value_expr));
let op = self.compound_operation_converstion(&assign.operation)?;
let new_value = Expression::Binary(BinaryExpression {
left,
right,
op,
span: assign.span.clone(),
});
Ok(AssignStatement {
operation: AssignOperation::Assign,
assignee,
value: new_value,
span: assign.span.clone(),
})
}
Expression::ArrayInline(_) => Ok(AssignStatement {
value => Ok(AssignStatement {
operation: AssignOperation::Assign,
assignee,
value,
span: assign.span.clone(),
}),
_ => Ok(assign.clone()),
}
}

View File

@ -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,122 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
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 input_len = context.input.len();
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.from_range && input_len == 1 {
match context.input.remove(0) {
ConstrainedValue::Array(input) => {
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 let ConstrainedValue::Integer(int) = target {
println!("RTAAI T {}", int);
}
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();
{
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,
from_range: false,
};
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(())
}
}
_ => Err(StatementError::array_assign_interior_index(&context.span)),
}
} else if context.from_range && input_len != 0 {
if let Some(index) = index_resolved.to_usize() {
if index >= input_len {
return Err(StatementError::array_assign_index_bounds(
index,
input_len,
&context.span,
));
}
let target = context.input.remove(index);
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,
)))
}
} 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)?;
}
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(())
Err(StatementError::array_assign_interior_index(&context.span))
}
}
}

View File

@ -32,6 +32,8 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
start: Option<&'a Expression<'a>>,
stop: Option<&'a Expression<'a>>,
) -> Result<(), StatementError> {
context.from_range = true;
let start_index = start
.map(|start| self.enforce_index(cs, start, &context.span))
.transpose()?

View File

@ -35,6 +35,7 @@ struct ResolverContext<'a, 'b, F: PrimeField, G: GroupType<F>> {
remaining_accesses: &'b [&'b AssignAccess<'a>],
indicator: &'b Boolean,
operation: AssignOperation,
from_range: bool,
}
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
@ -68,8 +69,10 @@ impl<'a, F: PrimeField, G: GroupType<F>> 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())
@ -99,6 +102,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
remaining_accesses: &accesses[..],
indicator,
operation: assignee.operation,
from_range: false,
})?;
*self.get_mut(variable.id).unwrap() = target;
Ok(())

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,10 @@ function main() {
x += 20;
console.assert(x == 30u32);
let w = 3u32;
w += x;
console.assert(w == 33u32);
let y = [1u8, 2u8];
y[0] += 3u8;
console.assert(y[0] == 4u8);

View File

@ -718,11 +718,450 @@
"content": " x = \"test1\" == \"test2\";"
}
}
},
{
"Definition": {
"declaration_type": "Let",
"variable_names": [
{
"mutable": true,
"identifier": "{\"name\":\"z\",\"span\":\"{\\\"line_start\\\":6,\\\"line_stop\\\":6,\\\"col_start\\\":9,\\\"col_stop\\\":10,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\" let z = [1u8, 2u8, 3u8, 4u8];\\\"}\"}",
"span": {
"line_start": 6,
"line_stop": 6,
"col_start": 9,
"col_stop": 10,
"path": "",
"content": " let z = [1u8, 2u8, 3u8, 4u8];"
}
}
],
"type_": null,
"value": {
"ArrayInline": {
"elements": [
{
"Expression": {
"Value": {
"Integer": [
"U8",
"1",
{
"line_start": 6,
"line_stop": 6,
"col_start": 14,
"col_stop": 17,
"path": "",
"content": " let z = [1u8, 2u8, 3u8, 4u8];"
}
]
}
}
},
{
"Expression": {
"Value": {
"Integer": [
"U8",
"2",
{
"line_start": 6,
"line_stop": 6,
"col_start": 19,
"col_stop": 22,
"path": "",
"content": " let z = [1u8, 2u8, 3u8, 4u8];"
}
]
}
}
},
{
"Expression": {
"Value": {
"Integer": [
"U8",
"3",
{
"line_start": 6,
"line_stop": 6,
"col_start": 24,
"col_stop": 27,
"path": "",
"content": " let z = [1u8, 2u8, 3u8, 4u8];"
}
]
}
}
},
{
"Expression": {
"Value": {
"Integer": [
"U8",
"4",
{
"line_start": 6,
"line_stop": 6,
"col_start": 29,
"col_stop": 32,
"path": "",
"content": " let z = [1u8, 2u8, 3u8, 4u8];"
}
]
}
}
}
],
"span": {
"line_start": 6,
"line_stop": 6,
"col_start": 13,
"col_stop": 33,
"path": "",
"content": " let z = [1u8, 2u8, 3u8, 4u8];"
}
}
},
"span": {
"line_start": 6,
"line_stop": 6,
"col_start": 5,
"col_stop": 33,
"path": "",
"content": " let z = [1u8, 2u8, 3u8, 4u8];"
}
}
},
{
"Assign": {
"operation": "Assign",
"assignee": {
"identifier": "{\"name\":\"z\",\"span\":\"{\\\"line_start\\\":7,\\\"line_stop\\\":7,\\\"col_start\\\":5,\\\"col_stop\\\":6,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\" z[0..\\\\\\\"test\\\\\\\" == \\\\\\\"test\\\\\\\"? 2 : 2] = x[0..2];\\\"}\"}",
"accesses": [
{
"ArrayRange": [
{
"Value": {
"Implicit": [
"0",
{
"line_start": 7,
"line_stop": 7,
"col_start": 7,
"col_stop": 8,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
]
}
},
{
"Ternary": {
"condition": {
"Binary": {
"left": {
"ArrayInline": {
"elements": [
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 116
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 11,
"col_stop": 12,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
},
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 101
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 12,
"col_stop": 13,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
},
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 115
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 13,
"col_stop": 14,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
},
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 116
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 14,
"col_stop": 15,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
}
],
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 10,
"col_stop": 16,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
},
"right": {
"ArrayInline": {
"elements": [
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 116
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 21,
"col_stop": 22,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
},
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 101
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 22,
"col_stop": 23,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
},
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 115
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 23,
"col_stop": 24,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
},
{
"Expression": {
"Value": {
"Char": {
"character": {
"Scalar": 116
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 24,
"col_stop": 25,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
}
}
],
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 20,
"col_stop": 26,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
},
"op": "Eq",
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 10,
"col_stop": 26,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
},
"if_true": {
"Value": {
"Implicit": [
"2",
{
"line_start": 7,
"line_stop": 7,
"col_start": 28,
"col_stop": 29,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
]
}
},
"if_false": {
"Value": {
"Implicit": [
"2",
{
"line_start": 7,
"line_stop": 7,
"col_start": 32,
"col_stop": 33,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
]
}
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 10,
"col_stop": 33,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
]
}
],
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 5,
"col_stop": 34,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
},
"value": {
"ArrayRangeAccess": {
"array": {
"Identifier": "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":7,\\\"line_stop\\\":7,\\\"col_start\\\":37,\\\"col_stop\\\":38,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\" z[0..\\\\\\\"test\\\\\\\" == \\\\\\\"test\\\\\\\"? 2 : 2] = x[0..2];\\\"}\"}"
},
"left": {
"Value": {
"Implicit": [
"0",
{
"line_start": 7,
"line_stop": 7,
"col_start": 39,
"col_stop": 40,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
]
}
},
"right": {
"Value": {
"Implicit": [
"2",
{
"line_start": 7,
"line_stop": 7,
"col_start": 42,
"col_stop": 43,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
]
}
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 37,
"col_stop": 44,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
},
"span": {
"line_start": 7,
"line_stop": 7,
"col_start": 5,
"col_stop": 44,
"path": "",
"content": " z[0..\"test\" == \"test\"? 2 : 2] = x[0..2];"
}
}
}
],
"span": {
"line_start": 1,
"line_stop": 6,
"line_stop": 8,
"col_start": 17,
"col_stop": 2,
"path": "",
@ -731,11 +1170,11 @@
},
"span": {
"line_start": 1,
"line_stop": 6,
"line_stop": 8,
"col_start": 1,
"col_stop": 2,
"path": "",
"content": "function main() {\n...\n}\n\n\n"
"content": "function main() {\n...\n}\n\n\n\n\n"
}
}
}

View File

@ -3,4 +3,6 @@ function main() {
s[..2] = "he";
let x = false;
x = "test1" == "test2";
let z = [1u8, 2u8, 3u8, 4u8];
z[0.."test" == "test"? 2 : 2] = x[0..2];
}

View File

@ -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());
}

View File

@ -0,0 +1,23 @@
/*
namespace: Compile
expectation: Pass
input_file:
- input/complex_access.in
*/
function main (a: [u8; 8], b: [[u8; 3]; 3], c: [(u8, u32); 1]) -> bool {
a[2..6][1] = 87;
a[2..3] = [42u8];
a[6..][0] = 43u8;
b[0..2][0] = [1u8; 3];
b[1..][1][1..2][0] = 126;
b[1..][0] = [42, 43, 44];
c[..1][0].1 = 1;
return
a == [1u8, 2, 42, 87, 5, 6, 43, 8]
&& b == [[1u8, 1, 1], [42, 43, 44], [7, 126, 9]]
&& c == [(0u8, 1u32)];
}

View File

@ -0,0 +1,7 @@
[main]
a: [u8; 8] = [1u8, 2, 3, 4, 5, 6, 7, 8];
b: [[u8; 3]; 3] = [[1u8, 2, 3], [4, 5, 6], [7, 8, 9]];
c: [(u8, u32); 1] = [(0u8, 0u32)];
[registers]
out: bool = true;

View File

@ -0,0 +1,10 @@
/*
namespace: Compile
expectation: Pass
input_file: input/three_ones.in
*/
function main (a: [u8; 3]) -> bool {
let y = a[0..[0u8; 2] == [0u8; 2]? 2u8 : 2u8];
return y == [1u8, 1];
}

View File

@ -0,0 +1,18 @@
---
namespace: Compile
expectation: Pass
outputs:
- circuit:
num_public_variables: 0
num_private_variables: 239
num_constraints: 239
at: 61be39f73914fdb4059c5d5e5776840262ab56dc414aa09106ddfee1d27ba174
bt: 3f2d18346ecde92782f60a29649e187402d8f2059cf934b31f5606c4d4381883
ct: db5fb9e237a13bca372ce7d3f232661fdf07c8ae7ef3d99517438a3532f59a41
output:
- input_file: input/complex_access.in
output:
registers:
out:
type: bool
value: "true"

View File

@ -0,0 +1,18 @@
---
namespace: Compile
expectation: Pass
outputs:
- circuit:
num_public_variables: 0
num_private_variables: 39
num_constraints: 39
at: 6f3ffff33f4e513211e7a55cc9edcab3bc2d2a146c2b280981308bb69165f86f
bt: adde6ad1b603a50c986ec99b6edcc972138bb6239e58a1b88e931bc0165b2e8e
ct: 867e3f6ee1f26af954e7868633a595d319267d91afc5d7e2074fe641fabde1d6
output:
- input_file: input/three_ones.in
output:
registers:
x:
type: bool
value: "true"

View File

@ -4,11 +4,11 @@ expectation: Pass
outputs:
- circuit:
num_public_variables: 0
num_private_variables: 1087
num_constraints: 1350
at: aae29cb6b4a71a5cf49d3de006b415b9a7818a2e23191819692e8d2ee69d8be2
bt: 11262f31fcaa7950be43eded328287305e7cbcb73c19455808f08f38116cfdd6
ct: 01dfba9e0754ad9890117694c9b03f4646642140aa7b6c393b2e264e701c323e
num_private_variables: 1277
num_constraints: 1604
at: c5d2d5bbb85d0f7ba48170fdb652aff057db6d1babf378d8a548af874e0734ff
bt: 726f3c39d11da5bc8487b8b7228a55cb7819a47a9b674d41f9c756c6927fb55a
ct: 7c4917152b59cb9caa30bad7d5119a3e36c40f7a783fdb8ff602dd5447573cc7
output:
- input_file: input/index1.in
output: