diff --git a/asg/src/expression/array_range_access.rs b/asg/src/expression/array_range_access.rs index 773e8c66a9..c0810a2586 100644 --- a/asg/src/expression/array_range_access.rs +++ b/asg/src/expression/array_range_access.rs @@ -148,24 +148,28 @@ impl<'a> FromAst<'a, leo_ast::ArrayRangeAccessExpression> for ArrayRangeAccessEx _ => None, }; let const_right = match right.map(|x| x.const_value()) { - Some(Some(ConstValue::Int(value))) => { - let value = value.to_usize(); - if let Some(value) = value { - if value > parent_size { - return Err(AsgConvertError::array_index_out_of_bounds( - value, - &right.unwrap().span().cloned().unwrap_or_default(), - )); + Some(Some(ConstValue::Int(inner_value))) => { + let usize_value = inner_value.to_usize(); + if let Some(inner_value) = usize_value { + if inner_value > parent_size { + let error_span = if let Some(right) = right { + right.span().cloned().unwrap_or_default() + } else { + value.span.clone() + }; + return Err(AsgConvertError::array_index_out_of_bounds(inner_value, &error_span)); } else if let Some(left) = const_left { - if left > value { - return Err(AsgConvertError::array_index_out_of_bounds( - value, - &right.unwrap().span().cloned().unwrap_or_default(), - )); + if left > inner_value { + let error_span = if let Some(right) = right { + right.span().cloned().unwrap_or_default() + } else { + value.span.clone() + }; + return Err(AsgConvertError::array_index_out_of_bounds(inner_value, &error_span)); } } } - value + usize_value } None => Some(parent_size), _ => None, @@ -188,12 +192,14 @@ impl<'a> FromAst<'a, leo_ast::ArrayRangeAccessExpression> for ArrayRangeAccessEx )); } } - if let Some(value) = const_left { - if value + expected_len > parent_size { - return Err(AsgConvertError::array_index_out_of_bounds( - value, - &left.unwrap().span().cloned().unwrap_or_default(), - )); + if let Some(left_value) = const_left { + if left_value + expected_len > parent_size { + let error_span = if let Some(left) = left { + left.span().cloned().unwrap_or_default() + } else { + value.span.clone() + }; + return Err(AsgConvertError::array_index_out_of_bounds(left_value, &error_span)); } } length = Some(expected_len); diff --git a/docs/rfc/003-imports-stabilization.md b/docs/rfc/003-imports-stabilization.md index 5fe231a643..6ae6794969 100644 --- a/docs/rfc/003-imports-stabilization.md +++ b/docs/rfc/003-imports-stabilization.md @@ -25,9 +25,7 @@ that we suggest few changes to Leo CLI and Manifest: - allow custom names for imports to manually resolve name conflicts; - add "curve" and "proving system" sections to the Manifest; - add "include" and "exclude" parameters for "proving system" and "curve"; - -Later this solution can be improved by adding a lock-file which would lock -imported packages based on both their contents and version. +- add a lock file which would store imported dependencies and their relations; # Motivation @@ -109,12 +107,7 @@ To support updated Manifest new command should be added to Leo CLI. ```bash # pull imports -leo install -``` - -Alternatively it can be called `pull`. -``` -leo pull +leo fetch ``` ## Imports Restructurization @@ -155,6 +148,44 @@ first-program => author1-program@0.1.0 second-program => author2-program2@1.0.4 ``` +## Leo.lock + +For imports map to be generated and read by the Leo binary and then by the Leo compiler, +a lock file needs to be created. Lock file should be generated by the `leo fetch` command, +which will pull the dependencies, process their manifests, and put the required information +to the file in the root directory of the program called `Leo.lock`. + +Suggested structure of this file is similar to the Cargo.lock file: + +``` +[[package]] +name = "suit-mk2" +version = "0.2.0" +author = "ironman" +import_name = "suit-mk2" + +[package.dependencies] +garbage = "ironman-suit@0.1.0" + +[[package]] +name = "suit" +version = "0.1.0" +author = "ironman" +import_name = "garbage" +``` + +In the example above, you can see that all program dependencies are defined as an +array called `package`. Each of the dependencies contains main information about +it, including the `import_name` field which is the imported package's name in +the Leo program. Also, it stores relationships between these dependencies in the +field `dependencies`. + +The format described here allows the Leo binary to form an imports map which can be +passed to the compiler. + +It is important to note that Leo.lock file is created only when a package has dependencies. +For programs with no dependencies, a lock file is not required and not created. + ## Recursive Dependencies This improvement introduces recursive dependencies. To solve this case preemptively diff --git a/docs/rfc/005-countdown-loops.md b/docs/rfc/005-countdown-loops.md index 41f68d4ab9..26ce2a6798 100644 --- a/docs/rfc/005-countdown-loops.md +++ b/docs/rfc/005-countdown-loops.md @@ -94,6 +94,26 @@ for i in 0..5 {} for i in 0..=5 {} ``` +## Step and Direction + +We remark that the step of both counting-up and counting-down loops is implicitly 1; +that is, the loop variable is incremented or decremented by 1. + +Whether the loop counts up or down is determined by how the starting and ending bounds compare. +Note that the bounds are not necessarily literals; +they may be more complex `const` expressions, and thus in general their values are resolved at code flattening time. +Because of the type restrictions on bounds, their values are always non-negative integers. +If `S` is the integer value of the starting bound and `E` is the integer value of the ending bound, +there are several cases to consider: +1. If `S == E` and the ending bound is exclusive, there is no actual loop; the range is empty. +2. If `S == E` and the ending bound is inclusive, the loop consists of just one iteration; the loop counts neither up nor down. +3. If `S < E` and the ending bound is exclusive, the loop counts up, from `S` to `E-1`. +4. If `S < E` and the ending bound is inclusive, the loop counts up, from `S` to `E`. +5. If `S > E` and the ending bound is exclusive, the loop counts down, from `S` to `E+1`. +6. If `S > E` and the ending bound is inclusive, the loop counts down, from `S` to `E`. + +Cases 3 and 5 consist of one or more iterations; cases 4 and 6 consist of two or more iterations. + ## Example The code example demostrated in the Motivation part of this document diff --git a/tests/compiler/array/array_range_access_fail.leo b/tests/compiler/array/array_range_access_fail.leo new file mode 100644 index 0000000000..a73127b571 --- /dev/null +++ b/tests/compiler/array/array_range_access_fail.leo @@ -0,0 +1,12 @@ +/* +namespace: Compile +expectation: Fail +input_file: input/array_range_access_fail.in +*/ + +function main ( + const x: u32 +) { + const y = [1u8; 3]; + const z: [u8; 2] = y[..1u32][..x]; +} \ No newline at end of file diff --git a/tests/compiler/array/input/array_range_access_fail.in b/tests/compiler/array/input/array_range_access_fail.in new file mode 100644 index 0000000000..5bea7d78d8 --- /dev/null +++ b/tests/compiler/array/input/array_range_access_fail.in @@ -0,0 +1,2 @@ +[constants] +x: u32 = 1u32; \ No newline at end of file diff --git a/tests/expectations/compiler/compiler/array/array_range_access_fail.leo.out b/tests/expectations/compiler/compiler/array/array_range_access_fail.leo.out new file mode 100644 index 0000000000..4fa4f71894 --- /dev/null +++ b/tests/expectations/compiler/compiler/array/array_range_access_fail.leo.out @@ -0,0 +1,5 @@ +--- +namespace: Compile +expectation: Fail +outputs: + - " --> compiler-test:7:24\n |\n 7 | const z: [u8; 2] = y[..1u32][..x];\n | ^^^^^^^^^^^^^^\n |\n = array index out of bounds: '0'"