mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-23 15:15:47 +03:00
Merge branch 'master' of github.com:AleoHQ/leo into feature/console-refactor-and-outs
This commit is contained in:
commit
780a2744ec
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -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",
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -79,6 +79,24 @@ impl<'a> FromAst<'a, leo_ast::TernaryExpression> for TernaryExpression<'a> {
|
||||
value: &leo_ast::TernaryExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<TernaryExpression<'a>, 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
})
|
||||
}
|
||||
|
@ -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<leo_ast::IterationStatement> 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!(),
|
||||
|
@ -382,6 +382,7 @@ impl Canonicalizer {
|
||||
variable: iteration.variable.clone(),
|
||||
start,
|
||||
stop,
|
||||
inclusive: iteration.inclusive,
|
||||
block,
|
||||
span: iteration.span.clone(),
|
||||
})
|
||||
|
@ -359,6 +359,7 @@ pub trait ReconstructingReducer {
|
||||
variable,
|
||||
start,
|
||||
stop,
|
||||
inclusive: iteration.inclusive,
|
||||
block,
|
||||
span: iteration.span.clone(),
|
||||
})
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -53,7 +53,14 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
||||
.to_usize()
|
||||
.ok_or_else(|| StatementError::loop_index_const(&span))?;
|
||||
|
||||
for i in from..to {
|
||||
let iter: Box<dyn Iterator<Item = usize>> = 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();
|
||||
|
@ -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,
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
## Status
|
||||
|
||||
DRAFT
|
||||
FINAL
|
||||
|
||||
# Summary
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
## Status
|
||||
|
||||
DRAFT
|
||||
FINAL
|
||||
|
||||
# Summary
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
## Status
|
||||
|
||||
DRAFT
|
||||
FINAL
|
||||
|
||||
# Summary
|
||||
|
||||
|
@ -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 `<lower>..<upper>`,
|
||||
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.
|
||||
|
149
docs/rfc/006-arrays-without-size.md
Normal file
149
docs/rfc/006-arrays-without-size.md
Normal file
@ -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; <size>]`.
|
||||
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 `<expression>.length`, where `<expression>` is an expression of array type.
|
||||
A variation is `<expression>.length()`, if we want it look more like a built-in method on arrays.
|
||||
Yet another option is `length(<expression>)`, 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.
|
Binary file not shown.
@ -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. `=`)
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
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<AsgConvertError> 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,
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
3
tests/compiler/import/imports/lib.leo
Normal file
3
tests/compiler/import/imports/lib.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function ello() -> [char; 4] {
|
||||
return "ello";
|
||||
}
|
12
tests/compiler/import/string_import.leo
Normal file
12
tests/compiler/import/string_import.leo
Normal file
@ -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;
|
||||
}
|
29
tests/compiler/statements/all_loops.leo
Normal file
29
tests/compiler/statements/all_loops.leo
Normal file
@ -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;
|
||||
}
|
9
tests/compiler/statements/assign_ternary.leo
Normal file
9
tests/compiler/statements/assign_ternary.leo
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file: inputs/u32_3.in
|
||||
*/
|
||||
|
||||
function main(x: u32) {
|
||||
let x = true ? x: true;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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"
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user