1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

add tests, add book entry, fix inference

This commit is contained in:
schaeff 2022-01-11 11:48:16 +01:00
parent e5184a236b
commit 1cd049ac93
16 changed files with 179 additions and 37 deletions

View file

@ -79,12 +79,18 @@ impl<T: Field> fmt::Display for Value<T> {
), ),
Value::Tuple(elements) => { Value::Tuple(elements) => {
write!(f, "(")?; write!(f, "(")?;
for (i, e) in elements.iter().enumerate() { match elements.len() {
write!(f, "{},", e)?; 1 => write!(f, "{},", elements[0]),
if i < elements.len() - 1 { _ => write!(
write!(f, " ")?; f,
} "{}",
} elements
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
),
}?;
write!(f, ")") write!(f, ")")
} }
} }

View file

@ -107,9 +107,18 @@ field[3] a = [1, 2, 3]
field[2] b = a[1..3] // initialize an array copying a slice from `a` field[2] b = a[1..3] // initialize an array copying a slice from `a`
``` ```
### Tuples
A tuple is a composite datatype representing a numbered collection of values.
The following code shows an example of how to use tuples.
```zokrates
{{#include ../../../zokrates_cli/examples/book/tuples.zok}}
```
In tuple types and values, the trailing comma is optional, unless the tuple contains a single element, in which case it is mandatory.
### Structs ### Structs
A struct is a composite datatype representing a named collection of variables. Structs can be generic over constants, in order to wrap arrays of generic size. For more details on generic array sizes, see [constant generics](../language/generics.md) A struct is a composite datatype representing a named collection of values. Structs can be generic over constants, in order to wrap arrays of generic size. For more details on generic array sizes, see [constant generics](../language/generics.md). The contained variables can be of any type.
The contained variables can be of any type.
The following code shows an example of how to use structs. The following code shows an example of how to use structs.

View file

@ -0,0 +1,4 @@
def main() -> bool:
(field[2], bool) v = ([1, 2], true)
v.0 = [42, 43]
return v.1

View file

@ -643,12 +643,18 @@ impl<'ast> fmt::Display for Expression<'ast> {
} }
Expression::InlineTuple(ref exprs) => { Expression::InlineTuple(ref exprs) => {
write!(f, "(")?; write!(f, "(")?;
for (i, e) in exprs.iter().enumerate() { match exprs.len() {
write!(f, "{},", e)?; 1 => write!(f, "{},", exprs[0]),
if i < exprs.len() - 1 { _ => write!(
write!(f, " ")?; f,
} "{}",
} exprs
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
),
}?;
write!(f, ")") write!(f, ")")
} }
Expression::ArrayInitializer(ref e, ref count) => write!(f, "[{}; {}]", e, count), Expression::ArrayInitializer(ref e, ref count) => write!(f, "[{}; {}]", e, count),

View file

@ -25,12 +25,18 @@ impl<'ast> fmt::Display for UnresolvedType<'ast> {
UnresolvedType::Array(ref ty, ref size) => write!(f, "{}[{}]", ty, size), UnresolvedType::Array(ref ty, ref size) => write!(f, "{}[{}]", ty, size),
UnresolvedType::Tuple(ref elements) => { UnresolvedType::Tuple(ref elements) => {
write!(f, "(")?; write!(f, "(")?;
for (i, e) in elements.iter().enumerate() { match elements.len() {
write!(f, "{},", e)?; 1 => write!(f, "{},", elements[0]),
if i < elements.len() { _ => write!(
write!(f, " ")?; f,
} "{}",
} elements
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
),
}?;
write!(f, ")") write!(f, ")")
} }
UnresolvedType::User(ref id, ref generics) => { UnresolvedType::User(ref id, ref generics) => {

View file

@ -2817,6 +2817,9 @@ impl<'ast, T: Field> Checker<'ast, T> {
(TypedExpression::Struct(e1), TypedExpression::Struct(e2)) => { (TypedExpression::Struct(e1), TypedExpression::Struct(e2)) => {
Ok(BooleanExpression::StructEq(box e1, box e2).into()) Ok(BooleanExpression::StructEq(box e1, box e2).into())
} }
(TypedExpression::Tuple(e1), TypedExpression::Tuple(e2)) => {
Ok(BooleanExpression::TupleEq(box e1, box e2).into())
}
(TypedExpression::Uint(e1), TypedExpression::Uint(e2)) (TypedExpression::Uint(e1), TypedExpression::Uint(e2))
if e1.get_type() == e2.get_type() => if e1.get_type() == e2.get_type() =>
{ {

View file

@ -1,7 +1,7 @@
use crate::typed_absy::types::{ use crate::typed_absy::types::{
ArrayType, DeclarationArrayType, DeclarationConstant, DeclarationStructMember, ArrayType, DeclarationArrayType, DeclarationConstant, DeclarationStructMember,
DeclarationStructType, DeclarationType, GArrayType, GStructType, GTupleType, GType, DeclarationStructType, DeclarationTupleType, DeclarationType, GArrayType, GStructType,
GenericIdentifier, StructType, TupleType, Type, GTupleType, GType, GenericIdentifier, StructType, TupleType, Type,
}; };
use crate::typed_absy::UBitwidth; use crate::typed_absy::UBitwidth;
use crate::typed_absy::{ use crate::typed_absy::{
@ -40,7 +40,7 @@ trait IntegerInference: Sized {
fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)>; fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)>;
} }
impl<'ast, T> IntegerInference for Type<'ast, T> { impl<'ast, T: Clone> IntegerInference for Type<'ast, T> {
type Pattern = DeclarationType<'ast, T>; type Pattern = DeclarationType<'ast, T>;
fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)> { fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)> {
@ -67,12 +67,16 @@ impl<'ast, T> IntegerInference for Type<'ast, T> {
t.get_common_pattern(u) t.get_common_pattern(u)
.map_err(|(t, u)| (Type::Struct(t), Type::Struct(u)))?, .map_err(|(t, u)| (Type::Struct(t), Type::Struct(u)))?,
)), )),
(Type::Tuple(t), Type::Tuple(u)) => Ok(DeclarationType::Tuple(
t.get_common_pattern(u)
.map_err(|(t, u)| (Type::Tuple(t), Type::Tuple(u)))?,
)),
(t, u) => Err((t, u)), (t, u) => Err((t, u)),
} }
} }
} }
impl<'ast, T> IntegerInference for ArrayType<'ast, T> { impl<'ast, T: Clone> IntegerInference for ArrayType<'ast, T> {
type Pattern = DeclarationArrayType<'ast, T>; type Pattern = DeclarationArrayType<'ast, T>;
fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)> { fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)> {
@ -88,7 +92,7 @@ impl<'ast, T> IntegerInference for ArrayType<'ast, T> {
} }
} }
impl<'ast, T> IntegerInference for StructType<'ast, T> { impl<'ast, T: Clone> IntegerInference for StructType<'ast, T> {
type Pattern = DeclarationStructType<'ast, T>; type Pattern = DeclarationStructType<'ast, T>;
fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)> { fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)> {
@ -120,6 +124,22 @@ impl<'ast, T> IntegerInference for StructType<'ast, T> {
} }
} }
impl<'ast, T: Clone> IntegerInference for TupleType<'ast, T> {
type Pattern = DeclarationTupleType<'ast, T>;
fn get_common_pattern(self, other: Self) -> Result<Self::Pattern, (Self, Self)> {
Ok(DeclarationTupleType {
elements: self
.elements
.iter()
.zip(other.elements.iter())
.map(|(t, u)| t.clone().get_common_pattern(u.clone()))
.collect::<Result<Vec<_>, _>>()
.map_err(|_| (self, other))?,
})
}
}
impl<'ast, T: Field> TypedExpression<'ast, T> { impl<'ast, T: Field> TypedExpression<'ast, T> {
// return two TypedExpression, replacing IntExpression by FieldElement or Uint to try to align the two types if possible. // return two TypedExpression, replacing IntExpression by FieldElement or Uint to try to align the two types if possible.
// Post condition is that (lhs, rhs) cannot be made equal by further removing IntExpressions // Post condition is that (lhs, rhs) cannot be made equal by further removing IntExpressions

View file

@ -1454,12 +1454,18 @@ impl<'ast, T: fmt::Display> fmt::Display for TupleExpression<'ast, T> {
TupleExpressionInner::Identifier(ref var) => write!(f, "{}", var), TupleExpressionInner::Identifier(ref var) => write!(f, "{}", var),
TupleExpressionInner::Value(ref values) => { TupleExpressionInner::Value(ref values) => {
write!(f, "(")?; write!(f, "(")?;
for (i, v) in values.iter().enumerate() { match values.len() {
write!(f, "{},", v)?; 1 => write!(f, "{},", values[0]),
if i < values.len() - 1 { _ => write!(
write!(f, " ")?; f,
} "{}",
} values
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(", ")
),
}?;
write!(f, ")") write!(f, ")")
} }
TupleExpressionInner::FunctionCall(ref function_call) => { TupleExpressionInner::FunctionCall(ref function_call) => {

View file

@ -189,6 +189,7 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
match t { match t {
Array(array_type) => Ok(Array(self.fold_array_type(array_type)?)), Array(array_type) => Ok(Array(self.fold_array_type(array_type)?)),
Struct(struct_type) => Ok(Struct(self.fold_struct_type(struct_type)?)), Struct(struct_type) => Ok(Struct(self.fold_struct_type(struct_type)?)),
Tuple(tuple_type) => Ok(Tuple(self.fold_tuple_type(tuple_type)?)),
t => Ok(t), t => Ok(t),
} }
} }

View file

@ -425,12 +425,18 @@ impl<'ast, S, R: PartialEq<S>> PartialEq<GTupleType<S>> for GTupleType<R> {
impl<S: fmt::Display> fmt::Display for GTupleType<S> { impl<S: fmt::Display> fmt::Display for GTupleType<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(")?; write!(f, "(")?;
for (i, e) in self.elements.iter().enumerate() { match self.elements.len() {
write!(f, "{},", e)?; 1 => write!(f, "{},", self.elements[0]),
if i < self.elements.len() - 1 { _ => write!(
write!(f, " ")?; f,
} "{}",
} self.elements
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(", ")
),
}?;
write!(f, ")") write!(f, ")")
} }
} }

View file

@ -0,0 +1,5 @@
{
"entry_point": "./tests/tests/structs/constant.zok",
"curves": ["Bn128"],
"tests": []
}

View file

@ -0,0 +1,4 @@
def main():
State s = ([0; 16],)
s.0[0] = 0x00000001
return

View file

@ -0,0 +1,40 @@
{
"entry_point": "./tests/tests/structs/identity.zok",
"curves": ["Bn128", "Bls12_381", "Bls12_377", "Bw6_761"],
"tests": [
{
"input": {
"values": ["42", "0"]
},
"output": {
"Ok": {
"values": ["42", "0"]
}
}
},
{
"input": {
"values": ["42", "1"]
},
"output": {
"Ok": {
"values": ["42", "1"]
}
}
},
{
"input": {
"values": ["42", "3"]
},
"output": {
"Err": {
"UnsatisfiedConstraint": {
"left": "9",
"right": "3",
"error": "ArgumentBitness"
}
}
}
}
]
}

View file

@ -0,0 +1,2 @@
def main((field, bool,) a) -> ((field, bool,)):
return a

View file

@ -0,0 +1,20 @@
{
"entry_point": "./tests/tests/structs/member_order.zok",
"curves": ["Bn128"],
"tests": [
{
"abi": true,
"input": {
"values": [{
"a":true,
"b": "3"
}]
},
"output": {
"Ok": {
"values": []
}
}
}
]
}

View file

@ -0,0 +1,4 @@
// this tests the abi, checking that the fields of a (field, bool) instance get encoded in the right order
// if the the encoder reverses `0` and `1`, the boolean check ends up being done on the field value, which would fail
def main((field, bool) f):
return