diff --git a/Cargo.lock b/Cargo.lock index b38a1672f5..ae503006bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2475,9 +2475,9 @@ dependencies = [ [[package]] name = "snarkvm-curves" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f97e6d7dc07a949050728353f2de05fd2ea376515a866fb9f06cfde14803a91e" +checksum = "62fdf07998a6e88fce7a8139e8282fb6b2702e49624b6d7e10a5cc1c9f014264" dependencies = [ "derivative", "rand 0.8.4", @@ -2491,9 +2491,9 @@ dependencies = [ [[package]] name = "snarkvm-derives" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffcfd2f005ec8c25c627516dfe4245e330792d22a21f6176627309072cd65176" +checksum = "d42f4d5abda149130149ce3865aef23d380dfcdd97fea7f75fd79b0aee369b29" dependencies = [ "proc-macro-crate", "proc-macro-error", @@ -2536,9 +2536,9 @@ dependencies = [ [[package]] name = "snarkvm-fields" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab09f08aee0e85613772242a442ad7057f166c54150ac52d5d5fbe00df77f1b" +checksum = "2ecd058b3608140610276be1eb1139a032a1bb171969b3ad70550c471d3ae503" dependencies = [ "anyhow", "bincode", @@ -2649,9 +2649,9 @@ dependencies = [ [[package]] name = "snarkvm-utilities" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c47f9b27b3dadda802b019e494bd6ddb6b4b3cc001b402eb217b75d66ff7758f" +checksum = "15b7ecf6a7afdd60a45cbd5ffab75185c296d8a4bf5eb20f3912384b1d4027c2" dependencies = [ "anyhow", "bincode", diff --git a/Cargo.toml b/Cargo.toml index 68ab110f40..3129564ae8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ version = "1.5.2" version = "0.7.4" [dependencies.snarkvm-curves] -version = "0.7.4" +version = "0.7.5" default-features = false [dependencies.snarkvm-gadgets] diff --git a/asg/src/error/mod.rs b/asg/src/error/mod.rs index 5eb6f79c58..f3d9533ffa 100644 --- a/asg/src/error/mod.rs +++ b/asg/src/error/mod.rs @@ -17,7 +17,7 @@ //! Errors encountered when attempting to convert to an asg from an ast. use crate::Span; -use leo_ast::{FormattedError, LeoError}; +use leo_ast::{AstError, FormattedError, LeoError}; use leo_parser::SyntaxError; #[derive(Debug, Error)] @@ -28,6 +28,9 @@ pub enum AsgConvertError { #[error("{}", _0)] ImportError(FormattedError), + #[error("{}", _0)] + AstError(#[from] AstError), + #[error("{}", _0)] InternalError(String), @@ -201,6 +204,12 @@ impl AsgConvertError { Self::new_from_span(format!("array index out of bounds: '{}'", index), span) } + pub fn ternary_different_types(left: &str, right: &str, span: &Span) -> Self { + let message = format!("ternary sides had different types: left {}, right {}", left, right); + + Self::new_from_span(message, span) + } + pub fn unknown_array_size(span: &Span) -> Self { Self::new_from_span("array size cannot be inferred, add explicit types".to_string(), span) } diff --git a/asg/src/expression/ternary.rs b/asg/src/expression/ternary.rs index 56564a5a81..ee071f05e8 100644 --- a/asg/src/expression/ternary.rs +++ b/asg/src/expression/ternary.rs @@ -79,6 +79,24 @@ impl<'a> FromAst<'a, leo_ast::TernaryExpression> for TernaryExpression<'a> { value: &leo_ast::TernaryExpression, expected_type: Option>, ) -> Result, AsgConvertError> { + let if_true = Cell::new(<&Expression<'a>>::from_ast( + scope, + &*value.if_true, + expected_type.clone(), + )?); + let left: PartialType = if_true.get().get_type().unwrap().into(); + + let if_false = Cell::new(<&Expression<'a>>::from_ast(scope, &*value.if_false, expected_type)?); + let right = if_false.get().get_type().unwrap().into(); + + if left != right { + return Err(AsgConvertError::ternary_different_types( + &left.to_string(), + &right.to_string(), + &value.span, + )); + } + Ok(TernaryExpression { parent: Cell::new(None), span: Some(value.span.clone()), @@ -87,12 +105,8 @@ impl<'a> FromAst<'a, leo_ast::TernaryExpression> for TernaryExpression<'a> { &*value.condition, Some(Type::Boolean.partial()), )?), - if_true: Cell::new(<&Expression<'a>>::from_ast( - scope, - &*value.if_true, - expected_type.clone(), - )?), - if_false: Cell::new(<&Expression<'a>>::from_ast(scope, &*value.if_false, expected_type)?), + if_true, + if_false, }) } } diff --git a/asg/src/reducer/reconstructing_reducer.rs b/asg/src/reducer/reconstructing_reducer.rs index cf4e617ec4..755c2eed1c 100644 --- a/asg/src/reducer/reconstructing_reducer.rs +++ b/asg/src/reducer/reconstructing_reducer.rs @@ -341,6 +341,7 @@ pub trait ReconstructingReducerStatement<'a>: ReconstructingReducerExpression<'a variable: input.variable, start: Cell::new(start), stop: Cell::new(stop), + inclusive: input.inclusive, body: Cell::new(body), }) } diff --git a/asg/src/statement/iteration.rs b/asg/src/statement/iteration.rs index 5e080acc75..04d327606e 100644 --- a/asg/src/statement/iteration.rs +++ b/asg/src/statement/iteration.rs @@ -39,6 +39,7 @@ pub struct IterationStatement<'a> { pub variable: &'a Variable<'a>, pub start: Cell<&'a Expression<'a>>, pub stop: Cell<&'a Expression<'a>>, + pub inclusive: bool, pub body: Cell<&'a Statement<'a>>, } @@ -93,6 +94,7 @@ impl<'a> FromAst<'a, leo_ast::IterationStatement> for &'a Statement<'a> { variable, stop: Cell::new(stop), start: Cell::new(start), + inclusive: statement.inclusive, body: Cell::new( scope .context @@ -114,6 +116,7 @@ impl<'a> Into for &IterationStatement<'a> { variable: self.variable.borrow().name.clone(), start: self.start.get().into(), stop: self.stop.get().into(), + inclusive: self.inclusive, block: match self.body.get() { Statement::Block(block) => block.into(), _ => unimplemented!(), diff --git a/ast/src/reducer/canonicalization.rs b/ast/src/reducer/canonicalization.rs index 9ca30d2aa2..4c78012c30 100644 --- a/ast/src/reducer/canonicalization.rs +++ b/ast/src/reducer/canonicalization.rs @@ -382,6 +382,7 @@ impl Canonicalizer { variable: iteration.variable.clone(), start, stop, + inclusive: iteration.inclusive, block, span: iteration.span.clone(), }) diff --git a/ast/src/reducer/reconstructing_reducer.rs b/ast/src/reducer/reconstructing_reducer.rs index 28277b470d..9bac0a88c7 100644 --- a/ast/src/reducer/reconstructing_reducer.rs +++ b/ast/src/reducer/reconstructing_reducer.rs @@ -359,6 +359,7 @@ pub trait ReconstructingReducer { variable, start, stop, + inclusive: iteration.inclusive, block, span: iteration.span.clone(), }) diff --git a/ast/src/statements/iteration.rs b/ast/src/statements/iteration.rs index a935f959f3..a97941ac55 100644 --- a/ast/src/statements/iteration.rs +++ b/ast/src/statements/iteration.rs @@ -24,16 +24,18 @@ pub struct IterationStatement { pub variable: Identifier, pub start: Expression, pub stop: Expression, + pub inclusive: bool, pub block: Block, pub span: Span, } impl fmt::Display for IterationStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let eq = if self.inclusive { "=" } else { "" }; write!( f, - "for {} in {}..{} {}", - self.variable, self.start, self.stop, self.block + "for {} in {}..{}{} {}", + self.variable, self.start, eq, self.stop, self.block ) } } diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 7f4cfb0580..60291bbd25 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -57,11 +57,11 @@ version = "1.5.2" version = "0.4" [dependencies.snarkvm-curves] -version = "0.7.4" +version = "0.7.5" default-features = false [dependencies.snarkvm-fields] -version = "0.7.4" +version = "0.7.5" default-features = false [dependencies.snarkvm-dpc] diff --git a/compiler/src/statement/iteration/iteration.rs b/compiler/src/statement/iteration/iteration.rs index fb3750c451..af4ffde859 100644 --- a/compiler/src/statement/iteration/iteration.rs +++ b/compiler/src/statement/iteration/iteration.rs @@ -53,7 +53,14 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { .to_usize() .ok_or_else(|| StatementError::loop_index_const(&span))?; - for i in from..to { + let iter: Box> = match (from < to, statement.inclusive) { + (true, true) => Box::new(from..=to), + (true, false) => Box::new(from..to), + (false, true) => Box::new((to..=from).rev()), + (false, false) => Box::new((to..from).rev()), + }; + + for i in iter { // Store index in current function scope. // For loop scope is not implemented. let variable = statement.variable.borrow(); diff --git a/compiler/src/test.rs b/compiler/src/test.rs index bcb16331c6..5d7d7eb2a6 100644 --- a/compiler/src/test.rs +++ b/compiler/src/test.rs @@ -231,6 +231,8 @@ impl Namespace for CompileNamespace { .unwrap_or_else(|_| "Error converting ast to string.".to_string()), ); + std::fs::remove_dir_all(std::path::Path::new("/tmp/output")).expect("Error failed to clean up output dir."); + let final_output = CompileOutput { circuit: last_circuit.unwrap(), output: output_items, diff --git a/docs/rfc/002-bounded-recursion.md b/docs/rfc/002-bounded-recursion.md index 3fb820af83..3e56153b65 100644 --- a/docs/rfc/002-bounded-recursion.md +++ b/docs/rfc/002-bounded-recursion.md @@ -13,7 +13,7 @@ ## Status -DRAFT +FINAL # Summary diff --git a/docs/rfc/003-imports-stabilization.md b/docs/rfc/003-imports-stabilization.md index 6ae6794969..27a393c32f 100644 --- a/docs/rfc/003-imports-stabilization.md +++ b/docs/rfc/003-imports-stabilization.md @@ -13,7 +13,7 @@ ## Status -DRAFT +FINAL # Summary diff --git a/docs/rfc/004-integer-type-casts.md b/docs/rfc/004-integer-type-casts.md index 946c34c73f..81a99abe11 100644 --- a/docs/rfc/004-integer-type-casts.md +++ b/docs/rfc/004-integer-type-casts.md @@ -13,7 +13,7 @@ ## Status -DRAFT +FINAL # Summary diff --git a/docs/rfc/005-countdown-loops.md b/docs/rfc/005-countdown-loops.md index 26ce2a6798..2cbb3a4a6a 100644 --- a/docs/rfc/005-countdown-loops.md +++ b/docs/rfc/005-countdown-loops.md @@ -13,7 +13,7 @@ ## Status -DRAFT +IMPLEMENTED # Summary @@ -22,8 +22,8 @@ This proposal suggests adding countdown loops and inclusive loop ranges into the # Motivation In the current design of the language only incremental ranges are allowed. Though -in some cases there's a need for loops going in the reverse direction. This example -demonstrates the shaker sort algorithm where countdown loops are mocked: +in some cases there's a need for loops going in the reverse direction. These examples +demonstrate the shaker sort and bubble sort algorithms where countdown loops are mocked: ```ts function shaker_sort(a: [u32; 10], const rounds: u32) -> [u32; 10] { @@ -49,7 +49,22 @@ function shaker_sort(a: [u32; 10], const rounds: u32) -> [u32; 10] { } ``` -Having a countdown loop in the example above could improve readability and +```ts +function bubble_sort(a: [u32; 10]) -> [u32; 10] { + for i in 0..9 { // i counts up + for j in 0..9-i { // i is flipped + if (a[j] > a[j+1]) { + let tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + return a +} +``` + +Having a countdown loop in the examples above could improve readability and usability of the language by making it more natural to the developer. However, if we imagined this example using a countdown loop, we would see that @@ -114,9 +129,9 @@ there are several cases to consider: Cases 3 and 5 consist of one or more iterations; cases 4 and 6 consist of two or more iterations. -## Example +## Examples -The code example demostrated in the Motivation part of this document +The code examples demostrated in the Motivation part of this document could be extended (or simplified) with the suggested syntax: ```ts @@ -142,7 +157,36 @@ function shaker_sort(a: [u32; 10], const rounds: u32) -> [u32; 10] { } ``` -## Possible Future Extension +```ts +function bubble_sort(a: [u32; 10]) -> [u32; 10] { + for i in 9..0 { // counts down + for j in 0..i { // no flipping + if (a[j] > a[j+1]) { + let tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + return a +} +``` + +# Drawbacks + +No obvious drawback. + +# Effect on Ecosystem + +Suggested change should have no effect on ecosystem because of its backward compatibility. + +# Alternatives + +## Mocking + +Coundown loops can be mocked manually. + +## Exclusive Starting Bounds While the ability to designate the ending bound of a loop as either exclusive or inclusive is critical as discussed below, we could also consider adding the ability to designate the starting bound of a loop as either exclusive or inclusive. @@ -158,7 +202,7 @@ The most symmetric but verbose approach is exemplified as follows: * `5>..=0` for `4 3 2 1 0` * `5=..>0` for `5 4 3 2 1` * `5>..>0` for `4 3 2 1` -That is, this approach makes exclusivensss an inclusiveness implicit. +That is, this approach makes exclusivensss and inclusiveness implicit. The use of `<` vs. `>` also indicates a loop direction, which can be inferred anyhow when the `const` bounds are resolved, so that would entail an additional consistency check, namely that the inequality sign/signs is/are consistent with the inferred loop direction. @@ -169,14 +213,80 @@ but that would be a different behavior from current Leo. We could instead go for different defaults for starting and ending bounds, i.e. `=` for the starting bound and `<` or `>` (depending on direction) for the ending bound. -# Drawbacks +A drawback of this approach is that it is somewhat verbose. +Furthermore, some of the authors of this RFC do not find it very readable. -No obvious drawback. +## Flipping Bound Defaults for Countdown -# Effect on Ecosystem +In the proposed design, there is an asymmetry between the treatment of loops that count up vs. down. +This can be seen clearly by thinking how to iterate through an array of size `N`: +```ts +for i in 0..n { ... a[i] ... } // count up -- 0 1 2 ... n-1 +for i in n-1..=0 { ... a[i] ... } // count down -- n-1 ... 2 1 0 +``` +While the loop that counts up has nice and simple bounds `0` and `n`, +the loop that counts down needs `n-1` and `=0`. -Suggested change should have no effect on ecosystem because of its backward compatibility. +So a possible idea is to use different defaults depending on the loop direction: +* For a loop that counts up: + * The starting (i.e. lower) bound is always inclusive. + * The ending (i.e. upper) bound is exclusive by default, inclusive with `=`. +* For loop that counts down: + * The ending (i.e. lower) bound is always inclusive. + * The starting (i.e. upper) bound is exclusive by default, inclusive with `=`. -# Alternatives +That is, different defaults apply to lower vs. upper bound, rather than to starting and ending bounds. -Coundown loops can be mocked manually. +Things become more symmetric in a way: +```ts +for i in 0..n { ... a[i] ... } // count up -- 0 1 2 ... n-1 +for i in n..0 { ... a[i] ... } // count down -- n-1 ... 2 1 0 +``` + +This is also consistent with Rust in a way, +where countdown loops are obtained by reversing the increasing range into a decreasing range, which flips the bounds. + +However, if we consider a possible extension in which the step may be larger than 1, we run into some awkwardness. +Imagine an extension in which `step` is specified: +```ts +for i in 10..0 step 2 ... // i = 8 6 4 2 0 -- starts at 10-2 = 8 +for i in 10..0 step 3 ... // i = 9 6 3 0 -- starts at 10-1 = 9 +``` + +Note how the actual starting index does not depend on starting/upper bound and step, +but rather on ending/lower bound and step, and must be calculated explicitly; +it doesn't "jump" at the reader. + +## Explicit Indication of Loop Direction + +Another idea that was brought up is to always write the range as `..`, +but include an explicit indication when the loop must count down, e.g. +```ts +for i in 0..n down { ... array[i] ... } // where 'down' indicates count down +``` + +The advantages are that +we retain the default that the first/lower bound is inclusive and the second/upper bound is exclusive, +and the direction is explicit and does not have to be inferred. +The direction matches starting/ending bound to lower/upper bound or upper/lower bound. + +But the awkwardness with larger steps than 1 remains: +```ts +for i in 0..10 down step 2 ... // i = 8 6 4 2 0 -- starts at 10-2 = 8 +for i in 0..10 down step 3 ... // i = 9 6 3 0 -- starts at 10-1 = 9 +``` + +## Variable in the Middle of Range with Equalities or Inequalities + +Another approach is to put the variable in the middle of the range, +along with equality or inequality signs around the variable, e.g. +```ts + for 0 <= i < 5 // 0 1 2 3 4 + for 0 <= i <= 5 // 0 1 2 3 4 5 + for 5 > i >= 0 // 4 3 2 1 0 +``` + +This maximizes explicitness, but it may need tweaking to avoid parsing ambiguities or difficulties +(recall that the bounds may be complex `const` expressions). + +This could be a future addition to consider, but it seems that it would not replace the current Rust-like syntax. diff --git a/docs/rfc/006-arrays-without-size.md b/docs/rfc/006-arrays-without-size.md new file mode 100644 index 0000000000..51e0e08f71 --- /dev/null +++ b/docs/rfc/006-arrays-without-size.md @@ -0,0 +1,149 @@ +# Leo RFC 006: Array Types with Unspecified Size + +## Authors + +- Max Bruce +- Collin Chin +- Alessandro Coglio +- Eric McCarthy +- Jon Pavlik +- Damir Shamanaev +- Damon Sicore +- Howard Wu + +## Status + +DRAFT + +# Summary + +This RFC proposes the addition, at the user level, of array types with unspecified size, +of the form `[T, _]`, where `T` is the element type and the underscore stands for an unspecified size. +It must be possible to infer the size at compile time. + +When these types are used in a function, +different calls of the function (which are inlined) may resolve the sizes of these types to different values. + +To make this extension more useful, this RFC also proposes the addition of +an operator to return the length of an array, whose result is resolved at compile time. + +# Motivation + +The initial motivation was the ability to have a type `string` for Leo strings, +which are currently represented as character arrays, +therefore requiring a size indication, i.e. `[char; ]`. +Allowing a `[char; _]` type, where `_` stands for an unspecified size, +makes it possible to define a type alias `string` for it, +once we also have an (orthogonal) extension of Leo to support type aliases. + +However, allowing `[T; _]` for any `T` (not just `char`) is a more generally useful feature. +This kind of types is already used internally in the Leo compiler. +Allowing their use externally should provide additional convenience to the user. +Some examples are shown in the 'Design' section. + +# Design + +User-facing array types currently have a specified size, indicated as a positive integer according to the grammar and static semantics +(the grammar allows 0, via the `natural` rule, but the static semantics checks that the natural is not 0). +Internally, the Leo compiler uses array types with unspecified size in some cases, e.g. when taking a slice with non-literal bounds. +These internal unspecified sizes must be resolved at compile time (by evaluating the constant bound expressions), in order for compilation to succeed. + +This RFC proposes to make array types with unspecified size available at the user level, +with the same requirement that their sizes must be resolved in order for compilation to succeed. + +The ABNF grammar changes as follows: +``` +; new rule: +array-dimension = natural / "_" + +; modified rule: +array-dimensions = array-dimension + / "(" array-dimension *( "," array-dimension ) ")" +``` +That is, an array dimension may be unspecified; this is also the case for multidimensional array types. + +Note that `array-dimension` is also referenced in this rule of the ABNF grammar: +``` +; existing rule: +array-repeat-construction = "[" expression ";" array-dimensions "]" +``` +The compiler will enforce, post-parsing, that array dimensions in array repeat expressions are positive integers, i.e. non-zero naturals. +This will be part of the static semantics of Leo. + +Array types may appear, either directly or within other types, in the following constructs: +- Constant declarations, global or local to functions. +- Variable declarations, local to functions. +- Function inputs. +- Function outputs. +- Member variable declarations. + +Thus, those are also the places where array types with unspecified size may occur. + +An array type with unspecified size that occurs in a global constant declaration must be resolved to a unique size. +On the other hand, an array type with unspecified size that occurs in a function +(whether a variable declaration, function input, or function output) +could be resolved to different sizes for different inlined calls of the function. +Finally, there seems to be no point in allowing array types of unspecified sizes in member variable declarations: +the circuit type must be completely known, including the types of its member variables; +therefore, this RFC prescribes that array types with unspecified size be disallowed in member variable declarations. +(This may be revisited if a good use case, and procedure for resolution, comes up.) + +## Examples + +In the following example, the array type with unspecified size obviates the need to explicate the size (3), +since it can be resolved by the compiler: +``` +let x: [u8; _] = [1, 2, 3]; +``` +Currently it is possible to omit the type of `x` altogether of course, +but then at least one of the elements must have a type suffix, e.g. `1u8`. + +Using an array type of unspecified size for a function input makes the function generic over the size: +``` +function f(x: [u8; _]) ... +``` +That is, `f` can take an array of `u8` of any size, and perform some generic computation on it, +because different inlined calls of `f` may resolve the size to different values (at compile time). +But this brings up the issue discussed below. + +## Array Size Operator + +Currently Leo has no array size operator, which makes sense because arrays have known sizes. +However, if we allow array types with unspecified size as explained above, +we may also need to extend Leo with an array size operator. + +However, consider a function `f` as above, which takes as input an array of `u8` of unspecified size. +In order to do something with the array, e.g. add all its elements and return the sum, +`f` should be able to access the size of the array. + +Thus, this RFC also proposed to extend Leo with such an operator. +A possibility is `.length`, where `` is an expression of array type. +A variation is `.length()`, if we want it look more like a built-in method on arrays. +Yet another option is `length()`, which is more like a built-in function. + +Note that the result of this operator can, and in fact must, be calculated at compile time; +not as part of the Leo interpreter, but rather as part of the flattening of Leo to R1CS. +In other words, this is really a compile-time operator, akin to `sizeof` in C. + +With that operator, the following function can be written: +``` +function f(x: [u8; _]) -> u8 { + let sum = 0u8; + for i in 0..length(x) { + sum += x[i]; + } + return sum; +} +``` + +# Drawbacks + +None, aside from inevitably making the language and compiler slightly more complex. + +# Effect on Ecosystem + +None. + +# Alternatives + +None. \ No newline at end of file diff --git a/grammar/README.md b/grammar/README.md index 6491e949ff..eef68c73a3 100644 Binary files a/grammar/README.md and b/grammar/README.md differ diff --git a/grammar/abnf-grammar.txt b/grammar/abnf-grammar.txt index 650a035744..233f0b4910 100644 --- a/grammar/abnf-grammar.txt +++ b/grammar/abnf-grammar.txt @@ -936,7 +936,7 @@ conditional-statement = branch ; that goes from a starting value (inclusive) to an ending value (exclusive). ; The body is a block. -loop-statement = %s"for" identifier %s"in" expression ".." expression block +loop-statement = %s"for" identifier %s"in" expression ".." [ "=" ] expression block ; An assignment statement is straightforward. ; Based on the operator, the assignment may be simple (i.e. `=`) diff --git a/imports/src/errors/import_parser.rs b/imports/src/errors/import_parser.rs index c91d06a006..c653d4dbf9 100644 --- a/imports/src/errors/import_parser.rs +++ b/imports/src/errors/import_parser.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . use leo_asg::AsgConvertError; -use leo_ast::{FormattedError, Identifier, LeoError, Span}; +use leo_ast::{AstError, FormattedError, Identifier, LeoError, Span}; use leo_parser::SyntaxError; use std::{io, path::Path}; @@ -26,6 +26,10 @@ pub enum ImportParserError { #[error("{}", _0)] SyntaxError(#[from] SyntaxError), + + #[error("{}", _0)] + AstError(#[from] AstError), + #[error("{}", _0)] AsgConvertError(#[from] AsgConvertError), } @@ -37,6 +41,7 @@ impl Into for ImportParserError { match self { ImportParserError::Error(x) => AsgConvertError::ImportError(x), ImportParserError::SyntaxError(x) => x.into(), + ImportParserError::AstError(x) => AsgConvertError::AstError(x), ImportParserError::AsgConvertError(x) => x, } } diff --git a/imports/src/parser/parse_symbol.rs b/imports/src/parser/parse_symbol.rs index 3a09506546..ce1f391f60 100644 --- a/imports/src/parser/parse_symbol.rs +++ b/imports/src/parser/parse_symbol.rs @@ -54,8 +54,10 @@ impl<'a> ImportParser<'a> { // Build the package abstract syntax tree. let program_string = &std::fs::read_to_string(&file_path).map_err(|x| ImportParserError::io_error(span, file_path_str, x))?; - let mut ast = leo_parser::parse(&file_path_str, &program_string)?; - ast.name = file_name; - Ok(ast) + let mut program = leo_parser::parse(&file_path_str, &program_string)?; + program.name = file_name; + let mut ast = leo_ast::Ast::new(program); + ast.canonicalize()?; + Ok(ast.into_repr()) } } diff --git a/parser/src/parser/statement.rs b/parser/src/parser/statement.rs index f4ec7ea566..d60d3e12bb 100644 --- a/parser/src/parser/statement.rs +++ b/parser/src/parser/statement.rs @@ -217,6 +217,7 @@ impl ParserContext { self.expect(Token::In)?; let start = self.parse_expression()?; self.expect(Token::DotDot)?; + let inclusive = self.eat(Token::Assign).is_some(); self.fuzzy_struct_state = true; let stop = self.parse_conditional_expression()?; self.fuzzy_struct_state = false; @@ -227,6 +228,7 @@ impl ParserContext { variable: ident, start, stop, + inclusive, block, }) } diff --git a/state/Cargo.toml b/state/Cargo.toml index b91debc546..9b588592db 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -29,7 +29,7 @@ version = "1.5.2" version = "0.7.4" [dependencies.snarkvm-curves] -version = "0.7.4" +version = "0.7.5" default-features = false [dependencies.snarkvm-dpc] diff --git a/synthesizer/Cargo.toml b/synthesizer/Cargo.toml index 383975948c..717af43e07 100644 --- a/synthesizer/Cargo.toml +++ b/synthesizer/Cargo.toml @@ -18,11 +18,11 @@ license = "GPL-3.0" edition = "2018" [dependencies.snarkvm-curves] -version = "0.7.4" +version = "0.7.5" default-features = false [dependencies.snarkvm-fields] -version = "0.7.4" +version = "0.7.5" default-features = false [dependencies.snarkvm-gadgets] diff --git a/tests/compiler/import/imports/lib.leo b/tests/compiler/import/imports/lib.leo new file mode 100644 index 0000000000..68d62cfc4b --- /dev/null +++ b/tests/compiler/import/imports/lib.leo @@ -0,0 +1,3 @@ +function ello() -> [char; 4] { + return "ello"; +} \ No newline at end of file diff --git a/tests/compiler/import/string_import.leo b/tests/compiler/import/string_import.leo new file mode 100644 index 0000000000..b073b36fc9 --- /dev/null +++ b/tests/compiler/import/string_import.leo @@ -0,0 +1,12 @@ +// namespace: Compile +// expectation: Pass +// input_file: input/dummy.in +// cwd: imports + +import lib.ello; + +function main(y: bool) -> bool { + let ello_str = ello(); + console.log("{}", ello_str); + return ello_str == "ello" && y; +} \ No newline at end of file diff --git a/tests/compiler/statements/all_loops.leo b/tests/compiler/statements/all_loops.leo new file mode 100644 index 0000000000..994d0f8264 --- /dev/null +++ b/tests/compiler/statements/all_loops.leo @@ -0,0 +1,29 @@ +/* +namespace: Compile +expectation: Pass +input_file: inputs/dummy.in +*/ + +function main(k: bool) -> bool { + let reverse: u32 = 0; + for i in 10..0 { + reverse += i; + } + + let forward: u32 = 0; + for x in 0..10 { + forward += x; + } + + let reverse_inclusive: u32 = 0; + for a in 10..=0 { + reverse_inclusive += a; + } + + let forward_inclusive: u32 = 0; + for b in 0..=10 { + forward_inclusive += b; + } + + return (reverse == forward) && (reverse_inclusive == forward_inclusive) && k; +} \ No newline at end of file diff --git a/tests/compiler/statements/assign_ternary.leo b/tests/compiler/statements/assign_ternary.leo new file mode 100644 index 0000000000..ed49709cb4 --- /dev/null +++ b/tests/compiler/statements/assign_ternary.leo @@ -0,0 +1,9 @@ +/* +namespace: Compile +expectation: Fail +input_file: inputs/u32_3.in +*/ + +function main(x: u32) { + let x = true ? x: true; +} \ No newline at end of file diff --git a/tests/expectations/compiler/compiler/circuits/pedersen_mock.leo.out b/tests/expectations/compiler/compiler/circuits/pedersen_mock.leo.out index 5e87294e05..bec7e9cfc6 100644 --- a/tests/expectations/compiler/compiler/circuits/pedersen_mock.leo.out +++ b/tests/expectations/compiler/compiler/circuits/pedersen_mock.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: e04195ede456babe4121950806163d2cc8c1e4b0180a37969099749ae3dfc3b9 - canonicalized_ast: a8673bb8b05eb0289435b8b2335402f022df9ff46db523707cc61eca1b9679ad - type_inferenced_ast: afeabed72941728dc8a8cc0ed27d1e8170f7dd477275be29e794b6c28142f2a7 + initial_ast: c983e5e79b4325ac133ac1a5ff0b1655c646111389991286322b8c16c5833837 + canonicalized_ast: 8dcb714238ef7e9fd3c66a7a12ec4621bb4e9ac5994f1c692215a9f93463ce9e + type_inferenced_ast: ebd34799bd1c6936ca5032812d2466bade58df616cc06e3c6e57151a06b78601 diff --git a/tests/expectations/compiler/compiler/function/iteration.leo.out b/tests/expectations/compiler/compiler/function/iteration.leo.out index 78b352d66f..16dc886ac5 100644 --- a/tests/expectations/compiler/compiler/function/iteration.leo.out +++ b/tests/expectations/compiler/compiler/function/iteration.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: 7dcfb3d46fc2a3f91d57f21db97e0d3829d0ac438a09fe0beaaa83aa0c545947 - canonicalized_ast: 33ffbf095e9050dd55b3e5cae0518f47b61689a7cefb5dd2d454ff6c4bb2fc9c - type_inferenced_ast: a6cff2181d7642f4a729d2301708c97956fc60322c8680a7d2d71296f4e7a5c6 + initial_ast: 96155896f4993bd97a8e15281e92c4e9352ed02343bccddb9c3cd0f8ca55b408 + canonicalized_ast: ae4a2dfa82f00621192f117bea664e58768d57376b68a90ce0e15c5bc9535e22 + type_inferenced_ast: 2452ad985e23efbc07a94f2945d406a959aca68ec37e9c349a30edcc12839c04 diff --git a/tests/expectations/compiler/compiler/function/iteration_repeated.leo.out b/tests/expectations/compiler/compiler/function/iteration_repeated.leo.out index 5a58598d01..8056fc7df4 100644 --- a/tests/expectations/compiler/compiler/function/iteration_repeated.leo.out +++ b/tests/expectations/compiler/compiler/function/iteration_repeated.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: cb4033ff1a05ae4b09fd9bbbeddd11b0fddececb98f96c7aef2ebb3069748870 - canonicalized_ast: 6607510f1df2682091711014cb0b1db8b786707accbab9c1760d710bed32ca17 - type_inferenced_ast: 4b5f83f6029a04c1fbce48e596ac2d7d0d97b87489b05656ff8773b04d2597fd + initial_ast: 55bf6a745bd0da1684239dd5f624a5ead1d5644c15f25e3099ff40c71ce23317 + canonicalized_ast: 929e1e876d7dd5d04ae39dd8d4b0b3fa3f0e8783e39056941115fff337a0ef84 + type_inferenced_ast: bc6903d0764db54fd4938835120ab6fa02575b1b896828876153bb30da28a19a diff --git a/tests/expectations/compiler/compiler/statements/all_loops.leo.out b/tests/expectations/compiler/compiler/statements/all_loops.leo.out new file mode 100644 index 0000000000..ead54a1743 --- /dev/null +++ b/tests/expectations/compiler/compiler/statements/all_loops.leo.out @@ -0,0 +1,21 @@ +--- +namespace: Compile +expectation: Pass +outputs: + - circuit: + num_public_variables: 0 + num_private_variables: 1 + num_constraints: 1 + at: 042610d0fd1fe6d6ac112138f8755752f44c7d2a00f1b5960574d6da5cda393f + bt: e97756698880ab7555a959a5fb5c6b4e15bd64612aa677adbfe2d0bd91f0a83c + ct: cf1cbb66a638b4860a516671fb74850e6ccf787fe6c4c8d29e9c04efe880bd05 + output: + - input_file: inputs/dummy.in + output: + registers: + r0: + type: bool + value: "true" + initial_ast: 7ecd56e44c6b1cb4f702a2712a2a7fe3bf564dac26a70e1f14151a688fb24769 + canonicalized_ast: 360ecac21f95b0e1d949d49458f2c4557446d01db08333d6f25621ef47a78a66 + type_inferenced_ast: 853fdefe080011aaa35363e827a527ab7ce6c7e93cd568c8a150211fa6e402fe diff --git a/tests/expectations/compiler/compiler/statements/assign_ternary.leo.out b/tests/expectations/compiler/compiler/statements/assign_ternary.leo.out new file mode 100644 index 0000000000..6518a49fff --- /dev/null +++ b/tests/expectations/compiler/compiler/statements/assign_ternary.leo.out @@ -0,0 +1,5 @@ +--- +namespace: Compile +expectation: Fail +outputs: + - " --> compiler-test:4:13\n |\n 4 | let x = true ? x: true;\n | ^^^^^^^^^^^^^^\n |\n = ternary sides had different types: left u32, right bool" diff --git a/tests/expectations/compiler/compiler/statements/for_loop.leo.out b/tests/expectations/compiler/compiler/statements/for_loop.leo.out index 4cedf331b1..3f9284533d 100644 --- a/tests/expectations/compiler/compiler/statements/for_loop.leo.out +++ b/tests/expectations/compiler/compiler/statements/for_loop.leo.out @@ -16,6 +16,6 @@ outputs: a: type: bool value: "true" - initial_ast: 8374ac96fbf6d918998ebe6ccb88842f2141efbc721314ff1b98a6e57c30ac8c - canonicalized_ast: 031149b98d685b26166d621c2206142438479414eeae8db5f92927aabca5d684 - type_inferenced_ast: 94959ec2fc0be13f8943a54261f6c23c6585773bc14936ee68a3f7289cc8bd26 + initial_ast: cce56dc8d678cd645e8fb3043aeb95bd24f94640e08219f00b7b9db66498ec29 + canonicalized_ast: 914f4ba5fc0c5148f1e105af4f857b8b12d29a609d47a366c73fd3d2f8eda3bf + type_inferenced_ast: 02b16a3b3a7e7c7ca4803292ea1f5410f293f2000f21c0a5245f2e5273561f06 diff --git a/tests/expectations/compiler/compiler/statements/iteration_basic.leo.out b/tests/expectations/compiler/compiler/statements/iteration_basic.leo.out index 18852b93e4..34b078aed4 100644 --- a/tests/expectations/compiler/compiler/statements/iteration_basic.leo.out +++ b/tests/expectations/compiler/compiler/statements/iteration_basic.leo.out @@ -16,6 +16,6 @@ outputs: a: type: bool value: "true" - initial_ast: 2af258377cc5af8e1bed75bed8dd57d2ecb969f71b8081a321a946df782ead97 - canonicalized_ast: 8733ab382dd7d3d506f999dd6b34442200e447cb530cc6d8115da6267b176f2e - type_inferenced_ast: 6f4c12b70f5fb244fbca65cb32e68c88ced6bf289afb7b1639257bb8de9a4bbb + initial_ast: 1c9abdc059f6649b381be12d435129702a048f941b5766212105d9f20c6479cf + canonicalized_ast: 5a8d9b146b28300345c3f0878f712ccc7a545534dc820d22180ec7bff8b96713 + type_inferenced_ast: 259654bcec24b6110db951fef9ee504455219c9e1860cc96b30d18ecf8fc2ead diff --git a/tests/expectations/compiler/compiler/statements/iteration_variable.leo.out b/tests/expectations/compiler/compiler/statements/iteration_variable.leo.out index 0bd6e7c478..74a9d729cf 100644 --- a/tests/expectations/compiler/compiler/statements/iteration_variable.leo.out +++ b/tests/expectations/compiler/compiler/statements/iteration_variable.leo.out @@ -16,6 +16,6 @@ outputs: a: type: bool value: "true" - initial_ast: 5223011087228b526071851038df390424ca442c4b21e0001cdf14bf8c63cf2c - canonicalized_ast: ab0a3a3caa5250c522c9eaa4b4bee5e36c179f856b02c2cdf94b4798344daf3d - type_inferenced_ast: 5825d1694362e1dab8710cbfa4feb608f968a98052a01fa36c152e06c1bd2cc4 + initial_ast: 3ac3ba48fb628fff67489c5d1502c3e0aaf9e197bf54d8c544fc79d9c83e3e16 + canonicalized_ast: c081654e7bd5edbfc1653444e73bc2f5e5b4e887f3e99dbb54f26cbfa37d84e1 + type_inferenced_ast: 23cddd23376a87be6e293bb4eae4470c9e450984bace87dc7386a5bf37081af0 diff --git a/tests/expectations/compiler/compiler/statements/reverse_loop.leo.out b/tests/expectations/compiler/compiler/statements/reverse_loop.leo.out new file mode 100644 index 0000000000..bb4416faf3 --- /dev/null +++ b/tests/expectations/compiler/compiler/statements/reverse_loop.leo.out @@ -0,0 +1,21 @@ +--- +namespace: Compile +expectation: Pass +outputs: + - circuit: + num_public_variables: 0 + num_private_variables: 1 + num_constraints: 1 + at: 042610d0fd1fe6d6ac112138f8755752f44c7d2a00f1b5960574d6da5cda393f + bt: e97756698880ab7555a959a5fb5c6b4e15bd64612aa677adbfe2d0bd91f0a83c + ct: cf1cbb66a638b4860a516671fb74850e6ccf787fe6c4c8d29e9c04efe880bd05 + output: + - input_file: inputs/dummy.in + output: + registers: + r0: + type: bool + value: "true" + initial_ast: 75fbb3ed1613fbf39ce803ff903befe209b26a953ba8db1d68e35066655d96e6 + canonicalized_ast: a96cae2c9e347245e07c63b5c94cf43497063c3f548c615627da4b1775e9a17b + type_inferenced_ast: dc663974ed1cce7b15c35eda29c9b1af5426eafddbd108b5a03cd7071ad4a6bc diff --git a/tests/expectations/parser/parser/statement/iteration.leo.out b/tests/expectations/parser/parser/statement/iteration.leo.out index 4fa2b3b899..045effafff 100644 --- a/tests/expectations/parser/parser/statement/iteration.leo.out +++ b/tests/expectations/parser/parser/statement/iteration.leo.out @@ -24,6 +24,7 @@ outputs: col_stop: 14 path: test content: "for x in 0..7 {}" + inclusive: false block: statements: [] span: @@ -62,6 +63,7 @@ outputs: col_stop: 14 path: test content: "for x in 0..7 {" + inclusive: false block: statements: - Return: @@ -119,6 +121,7 @@ outputs: col_stop: 17 path: test content: "for x in 0..99u8 {" + inclusive: false block: statements: - Return: @@ -167,6 +170,7 @@ outputs: content: "for x in 0..Self {" stop: Identifier: "{\"name\":\"Self\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":13,\\\"col_stop\\\":17,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"for x in 0..Self {\\\"}\"}" + inclusive: false block: statements: - Return: