diff --git a/zokrates_cli/examples/argument_reassign.code b/zokrates_cli/examples/argument_reassign.code index 60db55c6..645698fa 100644 --- a/zokrates_cli/examples/argument_reassign.code +++ b/zokrates_cli/examples/argument_reassign.code @@ -1,5 +1,5 @@ def sub(field a) -> (field): - field a = a + 3 + a = a + 3 return a def main() -> (field): diff --git a/zokrates_cli/examples/error/duplicate_declaration.code b/zokrates_cli/examples/error/duplicate_declaration.code new file mode 100644 index 00000000..919eb2a0 --- /dev/null +++ b/zokrates_cli/examples/error/duplicate_declaration.code @@ -0,0 +1,7 @@ +def foo() -> (field): + return 1 + +def main() -> (field): + bool a + field a = foo() + return 1 \ No newline at end of file diff --git a/zokrates_cli/examples/functions/expressions.code b/zokrates_cli/examples/functions/expressions.code index 9d650213..71340136 100644 --- a/zokrates_cli/examples/functions/expressions.code +++ b/zokrates_cli/examples/functions/expressions.code @@ -4,7 +4,7 @@ def add(field a,field b) -> (field): // Expected for inputs 1,1: c=4, d=7, e=10 def main(field a,field b) -> (field): - field c = add(a*2+3*b-a,b-1) - field d = add(a*b+2, a*b*c) - field e = add(add(a,d),add(a,b)) + c = add(a*2+3*b-a,b-1) + d = add(a*b+2, a*b*c) + e = add(add(a,d),add(a,b)) return e diff --git a/zokrates_cli/examples/functions/functions.code b/zokrates_cli/examples/functions/functions.code index 7a0b4cc9..ab0d8c91 100644 --- a/zokrates_cli/examples/functions/functions.code +++ b/zokrates_cli/examples/functions/functions.code @@ -2,5 +2,5 @@ def add(field f,field g) -> (field): return f+g def main(field a, field b) -> (field): - field c = add(a,b) + c = add(a,b) return c diff --git a/zokrates_cli/examples/functions/multi_functions.code b/zokrates_cli/examples/functions/multi_functions.code index e7f28cd0..d43ad5eb 100644 --- a/zokrates_cli/examples/functions/multi_functions.code +++ b/zokrates_cli/examples/functions/multi_functions.code @@ -4,10 +4,10 @@ def add(field a, field b) -> (field): def main(field a, field b,field c, field d) -> (field): field g = a + b - field x = add(a,b) - field y = add(c,d) - field g = add(x, g) + x = add(a,b) + y = add(c,d) + g = add(x, g) g = add(x, g) field f = c + d + a - field g = add(g+a, add(x,b)) + g = add(g+a, add(x,b)) return x + y + g + f diff --git a/zokrates_cli/examples/functions/multi_shadowing.code b/zokrates_cli/examples/functions/multi_shadowing.code index dea0b545..ca4edcd6 100644 --- a/zokrates_cli/examples/functions/multi_shadowing.code +++ b/zokrates_cli/examples/functions/multi_shadowing.code @@ -6,6 +6,6 @@ def sub(field a, field b) -> (field): return a-b def main(field a, field b) -> (field): - field c = add(a,b) - field d = sub(a,b) + c = add(a,b) + d = sub(a,b) return 0 diff --git a/zokrates_cli/examples/functions/no_args_multiple.code b/zokrates_cli/examples/functions/no_args_multiple.code index 544b16c7..9d466e1f 100644 --- a/zokrates_cli/examples/functions/no_args_multiple.code +++ b/zokrates_cli/examples/functions/no_args_multiple.code @@ -2,7 +2,7 @@ def const() -> (field): return 123123 def add(field a,field b) -> (field): - field a=const() + a=const() return a+b def main(field a,field b) -> (field): diff --git a/zokrates_cli/examples/reassignment.code b/zokrates_cli/examples/reassignment.code index 38e5c3c1..33e7945e 100644 --- a/zokrates_cli/examples/reassignment.code +++ b/zokrates_cli/examples/reassignment.code @@ -3,5 +3,5 @@ def main(field x) -> (field): field b = a + x a = 7 field c = a + b - field a = a + 5 + a = a + 5 return a + c diff --git a/zokrates_cli/examples/sha256/binary/fulladd.code b/zokrates_cli/examples/sha256/binary/fulladd.code index 211cca32..08186583 100644 --- a/zokrates_cli/examples/sha256/binary/fulladd.code +++ b/zokrates_cli/examples/sha256/binary/fulladd.code @@ -6,5 +6,5 @@ import "./or.code" as OR def main(field a, field b, field car) -> (field, field): out1, car1 = HALFADD(a, b) out2, car2 = HALFADD(out1, car) - field car3 = OR(car1, car2) + car3 = OR(car1, car2) return out2, car3 \ No newline at end of file diff --git a/zokrates_cli/examples/taxation.code b/zokrates_cli/examples/taxation.code index 27429ff0..f270b5e2 100644 --- a/zokrates_cli/examples/taxation.code +++ b/zokrates_cli/examples/taxation.code @@ -1,7 +1,7 @@ -def wtax(debt,wealth): - x = if wealth < debt then 0 else (wealth-debt) fi +def wtax(field debt, field wealth) -> (field): + field x = if wealth < debt then 0 else (wealth-debt) fi return x -def main(debt, wealth): - tax = wtax(debt,wealth) +def main(private field debt, private field wealth) -> (field): + field tax = wtax(debt,wealth) return tax \ No newline at end of file diff --git a/zokrates_cli/src/bin.rs b/zokrates_cli/src/bin.rs index 614c58a3..1c2fb470 100644 --- a/zokrates_cli/src/bin.rs +++ b/zokrates_cli/src/bin.rs @@ -550,31 +550,31 @@ mod tests { use super::*; use self::glob::glob; - // #[test] - // fn examples() { - // for p in glob("./examples/**/*.code").expect("Failed to read glob pattern") { - // let path = match p { - // Ok(x) => x, - // Err(why) => panic!("Error: {:?}", why), - // }; + #[test] + fn examples() { + for p in glob("./examples/**/*.code").expect("Failed to read glob pattern") { + let path = match p { + Ok(x) => x, + Err(why) => panic!("Error: {:?}", why), + }; - // if path.to_str().unwrap().contains("error") { - // continue - // } + if path.to_str().unwrap().contains("error") { + continue + } - // println!("Testing {:?}", path); + println!("Testing {:?}", path); - // let file = File::open(path.clone()).unwrap(); + let file = File::open(path.clone()).unwrap(); - // let mut reader = BufReader::new(file); - // let location = path.parent().unwrap().to_path_buf().into_os_string().into_string().unwrap(); + let mut reader = BufReader::new(file); + let location = path.parent().unwrap().to_path_buf().into_os_string().into_string().unwrap(); - // let program_flattened: FlatProg = - // compile(&mut reader, Some(location), Some(fs_resolve), true, false).unwrap(); + let program_flattened: FlatProg = + compile(&mut reader, Some(location), Some(fs_resolve), true, false).unwrap(); - // let (..) = r1cs_program(&program_flattened); - // } - // } + let (..) = r1cs_program(&program_flattened); + } + } #[test] fn examples_with_input_success() { diff --git a/zokrates_core/src/flatten/mod.rs b/zokrates_core/src/flatten/mod.rs index 9b672361..03beac5f 100644 --- a/zokrates_core/src/flatten/mod.rs +++ b/zokrates_core/src/flatten/mod.rs @@ -501,7 +501,7 @@ impl Flattener { } } panic!( - "TypedFunction definition for function {} with {:?} argument(s) not found.", + "TypedFunction definition for function {} with {:?} argument(s) not found. Should have been detected during semantic checking.", id, param_expressions ); @@ -743,7 +743,15 @@ impl Flattener { expr_subbed, ) }, - _ => panic!("Functions can only return expressions of type FieldElement") + TypedExpression::Boolean(e) => { + let expr_subbed = e.apply_substitution(&self.substitution); + self.flatten_boolean_expression( + functions_flattened, + arguments_flattened, + statements_flattened, + expr_subbed, + ) + } } }).collect(); @@ -764,26 +772,35 @@ impl Flattener { // define n variables with n the number of primitive types for v_type // assign them to the n primitive types for expr - match expr { + let rhs = match expr { TypedExpression::FieldElement(expr) => { let expr_subbed = expr.apply_substitution(&self.substitution); - let rhs = self.flatten_field_expression( + self.flatten_field_expression( functions_flattened, arguments_flattened, statements_flattened, expr_subbed, - ); - let var = self.use_variable(&v.id); - // handle return of function call - let var_to_replace = self.get_latest_var_substitution(&v.id); - if !(var == var_to_replace) && self.variables.contains(&var_to_replace) && !self.substitution.contains_key(&var_to_replace){ - self.substitution.insert(var_to_replace.clone().to_string(),var.clone()); - } - - statements_flattened.push(FlatStatement::Definition(var, rhs)); + ) }, - _ => panic!("Definitions must have type FieldElement") + TypedExpression::Boolean(expr) => { + let expr_subbed = expr.apply_substitution(&self.substitution); + self.flatten_boolean_expression( + functions_flattened, + arguments_flattened, + statements_flattened, + expr_subbed, + ) + }, + }; + + let var = self.use_variable(&v.id); + // handle return of function call + let var_to_replace = self.get_latest_var_substitution(&v.id); + if !(var == var_to_replace) && self.variables.contains(&var_to_replace) && !self.substitution.contains_key(&var_to_replace){ + self.substitution.insert(var_to_replace.clone().to_string(),var.clone()); } + + statements_flattened.push(FlatStatement::Definition(var, rhs)); } TypedStatement::Condition(expr1, expr2) => { @@ -831,7 +848,47 @@ impl Flattener { }; statements_flattened.push(FlatStatement::Condition(lhs, rhs)); }, - _ => panic!("Conditions (Assertions) must be applied to expressions of type FieldElement") + (TypedExpression::Boolean(e1), TypedExpression::Boolean(e2)) => { + + let e1_subbed = e1.apply_substitution(&self.substitution); + let e2_subbed = e2.apply_substitution(&self.substitution); + + let (lhs, rhs) = if e1_subbed.is_linear() { + ( + self.flatten_boolean_expression( + functions_flattened, + arguments_flattened, + statements_flattened, + e1_subbed + ), + self.flatten_boolean_expression( + functions_flattened, + arguments_flattened, + statements_flattened, + e2_subbed, + ), + ) + } else if e2_subbed.is_linear() { + ( + self.flatten_boolean_expression( + functions_flattened, + arguments_flattened, + statements_flattened, + e2_subbed, + ), + self.flatten_boolean_expression( + functions_flattened, + arguments_flattened, + statements_flattened, + e1_subbed, + ), + ) + } else { + unimplemented!() + }; + statements_flattened.push(FlatStatement::Condition(lhs, rhs)); + }, + _ => panic!("non matching types in condition should have been caught at semantic stage") } } TypedStatement::For(var, start, end, statements) => { @@ -862,7 +919,7 @@ impl Flattener { let rhs_subbed = rhs.apply_substitution(&self.substitution); match rhs_subbed { - TypedExpressionList::FunctionCall(fun_id, exprs, types) => { + TypedExpressionList::FunctionCall(fun_id, exprs, _) => { let rhs_flattened = self.flatten_function_call( functions_flattened, arguments_flattened, @@ -872,21 +929,15 @@ impl Flattener { &exprs, ); + // this will change for types that have multiple underlying fe for (i, v) in vars.into_iter().enumerate() { - let var_type = &types[i]; - - match var_type { - Type::FieldElement => { - let var = self.use_variable(&v.id); - // handle return of function call - let var_to_replace = self.get_latest_var_substitution(&v.id); - if !(var == var_to_replace) && self.variables.contains(&var_to_replace) && !self.substitution.contains_key(&var_to_replace){ - self.substitution.insert(var_to_replace.clone().to_string(),var.clone()); - } - statements_flattened.push(FlatStatement::Definition(var, rhs_flattened.expressions[i].clone())); - }, - _ => panic!("MultipleDefinition has to define expressions of type FieldElement") + let var = self.use_variable(&v.id); + // handle return of function call + let var_to_replace = self.get_latest_var_substitution(&v.id); + if !(var == var_to_replace) && self.variables.contains(&var_to_replace) && !self.substitution.contains_key(&var_to_replace){ + self.substitution.insert(var_to_replace.clone().to_string(),var.clone()); } + statements_flattened.push(FlatStatement::Definition(var, rhs_flattened.expressions[i].clone())); } }, } diff --git a/zokrates_core/src/semantics.rs b/zokrates_core/src/semantics.rs index 60e5f553..a3fbf636 100644 --- a/zokrates_core/src/semantics.rs +++ b/zokrates_core/src/semantics.rs @@ -184,7 +184,7 @@ impl Checker { 0 => { }, - _ => panic!("dupllicate function declaration should have been caught") + _ => panic!("duplicate function declaration should have been caught") } self.enter_scope(); @@ -413,7 +413,20 @@ impl Checker { (TypedExpression::Boolean(condition), TypedExpression::FieldElement(consequence), TypedExpression::FieldElement(alternative)) => { Ok(FieldElementExpression::IfElse(box condition, box consequence, box alternative).into()) }, - _ => panic!("id else only for bool fe fe") + (condition, consequence, alternative) => + Err( + Error { + message: + format!("if {{condition}} then {{consequence}} else {{alternative}} should have types {}, {}, {}, found {}, {}, {}", + Type::Boolean, + Type::FieldElement, + Type::FieldElement, + condition.get_type(), + consequence.get_type(), + alternative.get_type(), + ) + } + ) } }, &Expression::Number(ref n) => Ok(FieldElementExpression::Number(n.clone()).into()), @@ -443,9 +456,9 @@ impl Checker { let f = &candidates[0]; // the return count has to be 1 if f.signature.outputs.len() == 1 { - match f.signature.outputs[0] { - Type::FieldElement => return Ok(FieldElementExpression::FunctionCall(f.id.clone(), arguments_checked).into()), - _ => panic!("cannot return booleans") + return match f.signature.outputs[0] { + Type::FieldElement => Ok(FieldElementExpression::FunctionCall(f.id.clone(), arguments_checked).into()), + ref t => Err( Error { message: format!("Outside of assignments, functions must return a single element of type {}, found type {}", Type::FieldElement, t)}) } } Err(Error { message: format!("{} returns {} values but is called outside of a definition", f.id, f.signature.outputs.len()) }) diff --git a/zokrates_core/src/typed_absy/mod.rs b/zokrates_core/src/typed_absy/mod.rs index 99486cfd..f26729ad 100644 --- a/zokrates_core/src/typed_absy/mod.rs +++ b/zokrates_core/src/typed_absy/mod.rs @@ -258,15 +258,6 @@ impl Typed for TypedExpression { } } -impl TypedExpression { - pub fn is_linear(&self) -> bool { - match self { - TypedExpression::Boolean(e) => e.is_linear(), - TypedExpression::FieldElement(e) => e.is_linear() - } - } -} - pub trait MultiTyped { fn get_types(&self) -> &Vec;