Skip to content

Commit 1bcd765

Browse files
authored
Adds tuple type (#261)
* support tuple type
1 parent 2d76977 commit 1bcd765

File tree

16 files changed

+591
-127
lines changed

16 files changed

+591
-127
lines changed

examples/log.no

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ fn main(pub public_input: Field) -> Field {
1313
log(arr);
1414

1515
let thing = Thing { xx : public_input , yy: public_input + 1};
16-
log("formatted string with a number {} boolean {} arr {} struct {}" , 1234,true, arr , thing);
16+
17+
log(thing);
18+
19+
let tup = (1 , true , thing);
20+
log("formatted string with a number {} boolean {} arr {} tuple {} struct {}" , 1234 , true, arr, tup, thing);
1721

1822
return public_input + 1;
1923
}

examples/tuple.no

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
struct Thing {
2+
xx: Field,
3+
tuple_field: (Field,Bool)
4+
}
5+
6+
// return tuples from functions
7+
fn Thing.new(xx: Field , tup: (Field,Bool)) -> (Thing , (Field,Bool)) {
8+
return (
9+
Thing {
10+
xx: xx,
11+
tuple_field:tup
12+
},
13+
tup
14+
);
15+
}
16+
17+
fn generic_array_tuple_test(var : ([[Field;NN];LEN],Bool)) -> (Field , [Field;NN]) {
18+
let zero = 0;
19+
let result = if var[1] {var[0][LEN - 1][NN - 1]} else { var[0][LEN - 2][NN - 2] };
20+
return (result , var[0][LEN - 1]);
21+
}
22+
23+
// xx should be 0
24+
fn main(pub xx: [Field; 2]) -> Field {
25+
// creation of new tuple with different types
26+
let tup = (1, true);
27+
28+
// create nested tuples
29+
let nested_tup = ((false, [1,2,3]), 1);
30+
log(nested_tup); // (1, (true , [1,2,3]))
31+
32+
let incr = nested_tup[1]; // 1
33+
34+
// tuples can be input to function
35+
let mut thing = Thing.new(xx[1] , (xx[0] , xx[0] == 0));
36+
37+
// you can access a tuple type just like you access a array
38+
thing[0].tuple_field[0] += incr;
39+
log(thing[0].tuple_field[0]);
40+
let new_allocation = [xx,xx];
41+
let ret = generic_array_tuple_test((new_allocation, true));
42+
43+
assert_eq(thing[0].tuple_field[0] , 1);
44+
log(ret[1]); // logs xx i.e [0,123]
45+
46+
return ret[0];
47+
}

src/backends/mod.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
helpers::PrettyField,
1515
imports::FnHandle,
1616
parser::types::TyKind,
17-
utils::{log_array_type, log_custom_type, log_string_type},
17+
utils::{log_array_or_tuple_type, log_custom_type, log_string_type},
1818
var::{ConstOrCell, Value, Var},
1919
witness::WitnessEnv,
2020
};
@@ -467,8 +467,20 @@ pub trait Backend: Clone {
467467

468468
// Array
469469
Some(TyKind::Array(b, s)) => {
470-
let (output, remaining) =
471-
log_array_type(self, &var_info.var.cvars, b, *s, witness_env, typed, span)?;
470+
let mut typs = Vec::with_capacity(*s as usize);
471+
for _ in 0..(*s) {
472+
typs.push((**b).clone());
473+
}
474+
let (output, remaining) = log_array_or_tuple_type(
475+
self,
476+
&var_info.var.cvars,
477+
&typs,
478+
*s,
479+
witness_env,
480+
typed,
481+
span,
482+
false,
483+
)?;
472484
assert!(remaining.is_empty());
473485
println!("{dbg_msg}{}", output);
474486
}
@@ -504,6 +516,22 @@ pub trait Backend: Clone {
504516
println!("{dbg_msg}{}", output);
505517
}
506518

519+
Some(TyKind::Tuple(typs)) => {
520+
let len = typs.len();
521+
let (output, remaining) = log_array_or_tuple_type(
522+
self,
523+
&var_info.var.cvars,
524+
&typs,
525+
len as u32,
526+
witness_env,
527+
typed,
528+
span,
529+
true,
530+
)
531+
.unwrap();
532+
assert!(remaining.is_empty());
533+
println!("{dbg_msg}{}", output);
534+
}
507535
None => {
508536
return Err(Error::new(
509537
"log",

src/circuit_writer/ir.rs

+38-12
Original file line numberDiff line numberDiff line change
@@ -972,11 +972,11 @@ impl<B: Backend> IRWriter<B> {
972972
Ok(Some(res))
973973
}
974974

975-
ExprKind::ArrayAccess { array, idx } => {
976-
// retrieve var of array
975+
ExprKind::ArrayOrTupleAccess { container, idx } => {
976+
// retrieve var of container
977977
let var = self
978-
.compute_expr(fn_env, array)?
979-
.expect("array access on non-array");
978+
.compute_expr(fn_env, container)?
979+
.expect("container access on non-container");
980980

981981
// compute the index
982982
let idx_var = self
@@ -987,29 +987,41 @@ impl<B: Backend> IRWriter<B> {
987987
.ok_or_else(|| self.error(ErrorKind::ExpectedConstant, expr.span))?;
988988
let idx: usize = idx.try_into().unwrap();
989989

990-
// retrieve the type of the elements in the array
991-
let array_typ = self.expr_type(array).expect("cannot find type of array");
990+
// retrieve the type of the elements in the container
991+
let container_typ = self
992+
.expr_type(container)
993+
.expect("cannot find type of container");
992994

993-
let elem_type = match array_typ {
995+
// actual starting index for narrowing the var depends on the cotainer
996+
// for arrays it is just idx * elem_size as all elements are of same size
997+
// while for tuples we have to sum the sizes of all types up to that index
998+
let (start, len) = match container_typ {
994999
TyKind::Array(ty, array_len) => {
9951000
if idx >= (*array_len as usize) {
9961001
return Err(self.error(
9971002
ErrorKind::ArrayIndexOutOfBounds(idx, *array_len as usize - 1),
9981003
expr.span,
9991004
));
10001005
}
1001-
ty
1006+
let len = self.size_of(ty);
1007+
let start = idx * self.size_of(ty);
1008+
(start, len)
1009+
}
1010+
1011+
TyKind::Tuple(typs) => {
1012+
let mut starting_idx = 0;
1013+
for i in 0..idx {
1014+
starting_idx += self.size_of(&typs[i]);
1015+
}
1016+
(starting_idx, self.size_of(&typs[idx]))
10021017
}
10031018
_ => Err(Error::new(
10041019
"compute-expr",
1005-
ErrorKind::UnexpectedError("expected array"),
1020+
ErrorKind::UnexpectedError("expected container"),
10061021
expr.span,
10071022
))?,
10081023
};
10091024

1010-
// compute the size of each element in the array
1011-
let len = self.size_of(elem_type);
1012-
10131025
// compute the real index
10141026
let start = idx * len;
10151027

@@ -1074,6 +1086,20 @@ impl<B: Backend> IRWriter<B> {
10741086
let var = VarOrRef::Var(Var::new(cvars, expr.span));
10751087
Ok(Some(var))
10761088
}
1089+
1090+
ExprKind::TupleDeclaration(items) => {
1091+
let mut cvars = vec![];
1092+
1093+
for item in items {
1094+
let var = self.compute_expr(fn_env, item)?.unwrap();
1095+
let to_extend = var.value(self, fn_env).cvars.clone();
1096+
cvars.extend(to_extend);
1097+
}
1098+
1099+
let var = VarOrRef::Var(Var::new(cvars, expr.span));
1100+
1101+
Ok(Some(var))
1102+
}
10771103
}
10781104
}
10791105

src/circuit_writer/writer.rs

+48-15
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,15 @@ impl<B: Backend> CircuitWriter<B> {
332332
unreachable!("generic array should have been resolved")
333333
}
334334
TyKind::String(_) => todo!("String type is not supported for constraints"),
335+
TyKind::Tuple(types) => {
336+
let mut offset = 0;
337+
for ty in types {
338+
let size = self.size_of(ty);
339+
let slice = &input[offset..(offset + size)];
340+
self.constrain_inputs_to_main(slice, input_typ, span)?;
341+
offset += size;
342+
}
343+
}
335344
};
336345
Ok(())
337346
}
@@ -726,11 +735,11 @@ impl<B: Backend> CircuitWriter<B> {
726735
Ok(Some(res))
727736
}
728737

729-
ExprKind::ArrayAccess { array, idx } => {
730-
// retrieve var of array
738+
ExprKind::ArrayOrTupleAccess { container, idx } => {
739+
// retrieve var of container
731740
let var = self
732-
.compute_expr(fn_env, array)?
733-
.expect("array access on non-array");
741+
.compute_expr(fn_env, container)?
742+
.expect("container access on non-container");
734743

735744
// compute the index
736745
let idx_var = self
@@ -742,32 +751,41 @@ impl<B: Backend> CircuitWriter<B> {
742751
let idx: BigUint = idx.into();
743752
let idx: usize = idx.try_into().unwrap();
744753

745-
// retrieve the type of the elements in the array
746-
let array_typ = self.expr_type(array).expect("cannot find type of array");
754+
// retrieve the type of the elements in the container
755+
let container_typ = self
756+
.expr_type(container)
757+
.expect("cannot find type of container");
747758

748-
let elem_type = match array_typ {
759+
// actual starting index for narrowing the var depends on the cotainer
760+
// for arrays it is just idx * elem_size as all elements are of same size
761+
// while for tuples we have to sum the sizes of all types up to that index
762+
let (start, len) = match container_typ {
749763
TyKind::Array(ty, array_len) => {
750764
if idx >= (*array_len as usize) {
751765
return Err(self.error(
752766
ErrorKind::ArrayIndexOutOfBounds(idx, *array_len as usize - 1),
753767
expr.span,
754768
));
755769
}
756-
ty
770+
let len = self.size_of(ty);
771+
let start = idx * self.size_of(ty);
772+
(start, len)
773+
}
774+
775+
TyKind::Tuple(typs) => {
776+
let mut start = 0;
777+
for i in 0..idx {
778+
start += self.size_of(&typs[i]);
779+
}
780+
(start, self.size_of(&typs[idx]))
757781
}
758782
_ => Err(Error::new(
759783
"compute-expr",
760-
ErrorKind::UnexpectedError("expected array"),
784+
ErrorKind::UnexpectedError("expected container"),
761785
expr.span,
762786
))?,
763787
};
764788

765-
// compute the size of each element in the array
766-
let len = self.size_of(elem_type);
767-
768-
// compute the real index
769-
let start = idx * len;
770-
771789
// out-of-bound checks
772790
if start >= var.len() || start + len > var.len() {
773791
return Err(self.error(
@@ -830,6 +848,21 @@ impl<B: Backend> CircuitWriter<B> {
830848
let var = VarOrRef::Var(Var::new(cvars, expr.span));
831849
Ok(Some(var))
832850
}
851+
// exact copy of Array Declaration there is nothing really different at when looking it from a expression level
852+
// as both of them are just `Vec<Expr>`
853+
ExprKind::TupleDeclaration(items) => {
854+
let mut cvars = vec![];
855+
856+
for item in items {
857+
let var = self.compute_expr(fn_env, item)?.unwrap();
858+
let to_extend = var.value(self, fn_env).cvars.clone();
859+
cvars.extend(to_extend);
860+
}
861+
862+
let var = VarOrRef::Var(Var::new(cvars, expr.span));
863+
864+
Ok(Some(var))
865+
}
833866
}
834867
}
835868

src/error.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ pub enum ErrorKind {
268268
#[error("array accessed at index {0} is out of bounds (max allowed index is {1})")]
269269
ArrayIndexOutOfBounds(usize, usize),
270270

271+
#[error("tuple accessed at index {0} is out of bounds (max allowed index is {1})")]
272+
TupleIndexOutofBounds(usize, usize),
273+
271274
#[error(
272275
"one-letter variables or types are not allowed. Best practice is to use descriptive names"
273276
)]
@@ -327,8 +330,8 @@ pub enum ErrorKind {
327330
#[error("field access can only be applied on custom structs")]
328331
FieldAccessOnNonCustomStruct,
329332

330-
#[error("array access can only be performed on arrays")]
331-
ArrayAccessOnNonArray,
333+
#[error("array like access can only be performed on arrays or tuples")]
334+
AccessOnNonCollection,
332335

333336
#[error("struct `{0}` does not exist (are you sure it is defined?)")]
334337
UndefinedStruct(String),

src/inputs.rs

+16
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,22 @@ impl<B: Backend> CompiledCircuit<B> {
151151

152152
Ok(res)
153153
}
154+
// parsing for tuple function inputs from json
155+
(TyKind::Tuple(types), Value::Array(values)) => {
156+
if values.len() != types.len() {
157+
Err(ParsingError::ArraySizeMismatch(
158+
values.len(),
159+
types.len() as usize,
160+
))?
161+
}
162+
// making a vec with capacity allows for less number of reallocations
163+
let mut res = Vec::with_capacity(values.len());
164+
for (ty, val) in types.iter().zip(values) {
165+
let el = self.parse_single_input(val, ty)?;
166+
res.extend(el);
167+
}
168+
Ok(res)
169+
}
154170
(expected, observed) => {
155171
return Err(ParsingError::MismatchJsonArgument(
156172
expected.clone(),

0 commit comments

Comments
 (0)