Merge pull request #1656 from AleoHQ/remove/unsized-array

Remove/unsized array
This commit is contained in:
Collin Chin 2022-03-07 17:14:03 -08:00 committed by GitHub
commit 5e2ba78750
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 52 additions and 477 deletions

View File

@ -20,45 +20,12 @@ use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer};
use smallvec::{smallvec, SmallVec};
use std::{fmt, ops::Deref};
/// A single array dimension.
#[derive(Clone, Deserialize, Debug, PartialEq, Eq, Hash)]
pub enum Dimension {
/// The dimension is `_`, that is unspecified and syntactically unknown.
Unspecified,
/// The dimension was specified, e.g., `5` elements.
Number(PositiveNumber),
}
impl fmt::Display for Dimension {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Unspecified => write!(f, "_"),
Self::Number(num) => write!(f, "{}", num),
}
}
}
impl Dimension {
/// } Returns `Some(n)` unless the dimension is [`Unspecified`].
pub fn as_specified(&self) -> Option<&PositiveNumber> {
match self {
Self::Unspecified => None,
Self::Number(n) => Some(n),
}
}
/// Returns true if the dimension is known to be zero.
fn is_zero(&self) -> bool {
self.as_specified().filter(|n| n.is_zero()).is_some()
}
}
/// Specifies array dimensions for array [`Type`]s or in array initializer [`Expression`]s.
#[derive(Clone, Deserialize, Debug, PartialEq, Eq, Hash)]
pub struct ArrayDimensions(pub SmallVec<[Dimension; 1]>);
pub struct ArrayDimensions(pub SmallVec<[PositiveNumber; 1]>);
impl Deref for ArrayDimensions {
type Target = [Dimension];
type Target = [PositiveNumber];
fn deref(&self) -> &Self::Target {
&*self.0
@ -67,22 +34,17 @@ impl Deref for ArrayDimensions {
impl ArrayDimensions {
/// Returns a single-dimensional array dimension.
pub fn single(dim: Dimension) -> Self {
pub fn single(dim: PositiveNumber) -> Self {
Self(smallvec![dim])
}
/// Returns true if the dimensions are not [`Unspecified`].
pub fn is_specified(&self) -> bool {
!self.contains(&Dimension::Unspecified)
}
/// Returns `true` if there is an array dimension equal to zero.
pub fn is_zero(&self) -> bool {
self.iter().any(|d| d.is_zero())
}
/// Attempts to remove the first dimension from the array, or returns `None` if it doesn't.
pub fn remove_first(&mut self) -> Option<Dimension> {
pub fn remove_first(&mut self) -> Option<PositiveNumber> {
if self.is_empty() {
None
} else {
@ -91,12 +53,12 @@ impl ArrayDimensions {
}
/// Attempts to remove the last dimension from the array, or returns `None` if it doesn't.
pub fn remove_last(&mut self) -> Option<Dimension> {
pub fn remove_last(&mut self) -> Option<PositiveNumber> {
self.0.pop()
}
}
/// Custom Serializer for ArrayDimensios is required to ignore internal ArrayDimension nodes in the AST.
/// Custom Serializer for ArrayDimensions is required to ignore internal ArrayDimension nodes in the AST.
impl Serialize for ArrayDimensions {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@ -104,10 +66,7 @@ impl Serialize for ArrayDimensions {
{
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
for dim in self.0.iter() {
match dim {
Dimension::Number(num) => seq.serialize_element(&num)?,
Dimension::Unspecified => seq.serialize_element(&PositiveNumber { value: "0".into() })?,
}
seq.serialize_element(&dim)?;
}
seq.end()
}

View File

@ -88,32 +88,28 @@ impl TryFrom<(Type, Expression)> for InputValue {
}
if let Some(dimension) = array_init.dimensions.remove_first() {
if let Some(number) = dimension.as_specified() {
let size = number.value.parse::<usize>().unwrap();
let mut values = Vec::with_capacity(size);
let size = dimension.value.parse::<usize>().unwrap();
let mut values = Vec::with_capacity(size);
// For when Dimensions are specified in a canonical way: [[u8; 3], 2];
// Else treat as math notation: [u8; (2, 3)];
if array_init.dimensions.len() == 0 {
for _ in 0..size {
values.push(InputValue::try_from((*type_.clone(), *array_init.element.clone()))?);
}
// Faking canonical array init is relatively easy: instead of using a straightforward
// recursion, with each iteration we manually modify ArrayInitExpression cutting off
// dimension by dimension.
} else {
for _ in 0..size {
values.push(InputValue::try_from((
Type::Array(type_.clone(), array_init.dimensions.clone()),
Expression::ArrayInit(array_init.clone()),
))?);
}
};
Self::Array(values)
// For when Dimensions are specified in a canonical way: [[u8; 3], 2];
// Else treat as math notation: [u8; (2, 3)];
if array_init.dimensions.len() == 0 {
for _ in 0..size {
values.push(InputValue::try_from((*type_.clone(), *array_init.element.clone()))?);
}
// Faking canonical array init is relatively easy: instead of using a straightforward
// recursion, with each iteration we manually modify ArrayInitExpression cutting off
// dimension by dimension.
} else {
unreachable!("dimensions must be specified");
}
for _ in 0..size {
values.push(InputValue::try_from((
Type::Array(type_.clone(), array_init.dimensions.clone()),
Expression::ArrayInit(array_init.clone()),
))?);
}
};
Self::Array(values)
} else {
unreachable!("dimensions are checked for zero");
}
@ -132,14 +128,10 @@ impl TryFrom<(Type, Expression)> for InputValue {
Self::Tuple(elements)
}
(Type::Array(element_type, dimensions), Expression::ArrayInline(array_inline)) => {
(Type::Array(element_type, _dimensions), Expression::ArrayInline(array_inline)) => {
let mut elements = Vec::with_capacity(array_inline.elements.len());
let span = array_inline.span().clone();
if !dimensions.is_specified() {
return Err(InputError::array_dimensions_must_be_specified(&span).into());
}
for element in array_inline.elements.into_iter() {
if let SpreadOrExpression::Expression(value_expression) = element {
elements.push(Self::try_from((*element_type.clone(), value_expression))?);

View File

@ -89,11 +89,6 @@ impl Type {
let mut left_dims = left_dims.to_owned();
let mut right_dims = right_dims.to_owned();
// Unable to compare arrays with unspecified sizes.
if !left_dims.is_specified() || !right_dims.is_specified() {
return false;
}
// Remove the first element from both dimensions.
let left_first = left_dims.remove_first();
let right_first = right_dims.remove_first();

View File

@ -59,12 +59,12 @@ impl ParserContext<'_> {
/// Returns an [`ArrayDimensions`] AST node if the next tokens represent dimensions for an array type.
pub fn parse_array_dimensions(&mut self) -> Result<ArrayDimensions> {
Ok(if let Some(dim) = self.parse_array_dimension() {
Ok(if let Some((dim, _)) = self.eat_int() {
ArrayDimensions(smallvec![dim])
} else {
let mut had_item_err = false;
let (dims, _, span) = self.parse_paren_comma_list(|p| {
Ok(if let Some(dim) = p.parse_array_dimension() {
Ok(if let Some((dim, _)) = p.eat_int() {
Some(dim)
} else {
let token = p.expect_any()?;
@ -80,17 +80,6 @@ impl ParserContext<'_> {
})
}
/// Parses a basic array dimension, i.e., an integer or `_`.
fn parse_array_dimension(&mut self) -> Option<Dimension> {
if let Some((int, _)) = self.eat_int() {
Some(Dimension::Number(int))
} else if self.eat(Token::Underscore).is_some() {
Some(Dimension::Unspecified)
} else {
None
}
}
/// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a type.
/// Also returns the span of the parsed token.
pub fn parse_type(&mut self) -> Result<(Type, Span)> {

View File

@ -1002,34 +1002,24 @@ An array type consists of an element type
and an indication of dimensions.
There is either a single dimension,
or a tuple of one or more dimensions.
Each dimension is either a natural or is unspecified.
Each dimension is a natural.
<a name="array-type"></a>
```abnf
array-type = "[" type ";" array-type-dimensions "]"
array-type = "[" type ";" array-dimensions "]"
```
Go to: _[array-type-dimensions](#user-content-array-type-dimensions), [type](#user-content-type)_;
Go to: _[array-dimensions](#user-content-array-dimensions), [type](#user-content-type)_;
<a name="array-type-dimension"></a>
<a name="array-dimensions"></a>
```abnf
array-type-dimension = natural / "_"
array-dimensions = natural / "(" natural *( "," natural ) ")"
```
Go to: _[natural](#user-content-natural)_;
<a name="array-type-dimensions"></a>
```abnf
array-type-dimensions = array-type-dimension
/ "(" array-type-dimension
*( "," array-type-dimension ) [","] ")"
```
Go to: _[array-type-dimension](#user-content-array-type-dimension)_;
The keyword `Self` denotes the enclosing circuit type.
It is only allowed inside a circuit type declaration.
@ -1203,19 +1193,10 @@ Go to: _[expression](#user-content-expression)_;
<a name="array-repeat-construction"></a>
```abnf
array-repeat-construction = "[" expression ";" array-expression-dimensions "]"
array-repeat-construction = "[" expression ";" array-dimensions "]"
```
Go to: _[array-expression-dimensions](#user-content-array-expression-dimensions), [expression](#user-content-expression)_;
<a name="array-expression-dimensions"></a>
```abnf
array-expression-dimensions = natural
/ "(" natural *( "," natural ) ")"
```
Go to: _[natural](#user-content-natural)_;
Go to: _[array-dimensions](#user-content-array-dimensions), [expression](#user-content-expression)_;
<a name="array-construction"></a>

View File

@ -645,15 +645,11 @@ tuple-type = "(" [ type 1*( "," type ) ] ")"
; and an indication of dimensions.
; There is either a single dimension,
; or a tuple of one or more dimensions.
; Each dimension is either a natural or is unspecified.
; Each dimension is a natural.
array-type = "[" type ";" array-type-dimensions "]"
array-type = "[" type ";" array-dimensions "]"
array-type-dimension = natural / "_"
array-type-dimensions = array-type-dimension
/ "(" array-type-dimension
*( "," array-type-dimension ) [","] ")"
array-dimensions = natural / "(" natural *( "," natural ) ")"
; The keyword `Self` denotes the enclosing circuit type.
; It is only allowed inside a circuit type declaration.
@ -751,10 +747,7 @@ array-inline-construction = "["
array-inline-element = expression / "..." expression
array-repeat-construction = "[" expression ";" array-expression-dimensions "]"
array-expression-dimensions = natural
/ "(" natural *( "," natural ) ")"
array-repeat-construction = "[" expression ";" array-dimensions "]"
array-construction = array-inline-construction / array-repeat-construction

View File

@ -1,6 +1,5 @@
[main]
a: u32 = 1;
b: u32 = 2;
y: bool = true;
[registers]
r0: u32 = 0;
r0: bool = false;

View File

@ -1,5 +1,11 @@
// The 'hello-world' main function.
function main(a: u32, b: u32) -> u32 {
let c: u32 = a + b;
return c;
type str = [char; _];
function main(y: bool) -> bool {
let s = "abc";
return (first_el(s) == 'a') == y;
}
function first_el(s: str) -> char {
return s[0];
}

View File

@ -312,12 +312,4 @@ create_errors!(
msg: format!("Tried to assign to static member `{}`", member),
help: None,
}
/// For when arrays with unspecified size are used in main.
@formatted
input_array_size_must_be_specified {
args: (),
msg: "arrays in main function input must have known size",
help: None,
}
);

View File

@ -46,14 +46,6 @@ create_errors!(
help: None,
}
/// For when [`ArrayDimensions`] are not specified.
@formatted
array_dimensions_must_be_specified {
args: (),
msg: "array dimensions must be specified",
help: None,
}
/// For when array init is using spread.
@formatted
array_spread_is_not_allowed {

View File

@ -1,9 +0,0 @@
/*
namespace: Compile
expectation: Fail
*/
function main() {
let x: [u8; _] = [1u8,2];
let z: bool = x == [1u8,2,3]; // array size mismatch
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: input/dummy.in
*/
function main(y: bool) -> bool {
let d: [u8; _] = [1,2,3,4];
return d == [1,2,3,4];
}

View File

@ -1,13 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: input/dummy.in
*/
function main(y: bool) -> bool {
return (first_el([1,2,3,4]) == 1) == y;
}
function first_el(arr: [u8; _]) -> u8 {
return arr[0];
}

View File

@ -1,7 +0,0 @@
[main]
y: bool = true;
n: bool = false;
a: [char; 11] = "hello world";
[registers]
r0: bool = false;

View File

@ -1,9 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: input/dummy.in
*/
function main(a: [char; 11], y: bool) -> bool {
return y == (a.len() == 11);
}

View File

@ -1,17 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: input/dummy.in
*/
function main(y: bool) -> bool {
let x = 0u8;
for i in 0..strlen("I swear to god I had something for this") {
x += 1;
}
return (x == 39) == y;
}
function strlen(str: [char; _]) -> u32 {
return str.len();
}

View File

@ -1,16 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: input/dummy.in
*/
type str = [char; _];
function main(y: bool) -> bool {
let s = "abc";
return (first_el(s) == 'a') == y;
}
function first_el(s: str) -> char {
return s[0];
}

View File

@ -1,5 +0,0 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [ECMP0376093]: array sizes must match for comparison; left: 2, right: 3\n --> compiler-test:5:19\n |\n 5 | let z: bool = x == [1u8,2,3]; // array size mismatch\n | ^^^^^^^^^^^^^^"

View File

@ -1,22 +0,0 @@
---
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: input/dummy.in
output:
registers:
r0:
type: bool
value: "true"
initial_ast: 71e25416f4a31045a847f29df48d5e9cce19d0df31c479761f2387eca4bb759f
imports_resolved_ast: a408ca2965d8d63856b1f95385746d5be9825b646e7f97a5fd3203638681292c
canonicalized_ast: a408ca2965d8d63856b1f95385746d5be9825b646e7f97a5fd3203638681292c
type_inferenced_ast: 88e5b982b094f07cc0337812b4965b655c020bdebfa1ad7ac8fd2ddd3c730b8b

View File

@ -1,22 +0,0 @@
---
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: input/dummy.in
output:
registers:
r0:
type: bool
value: "true"
initial_ast: d6d3ebe6a0b7f19e51d245d715706122577777a8325459df7db2c08ee5f841bd
imports_resolved_ast: 675c0542777db276ce7a39decb9dc5aacfde6b00ebfeb3982c480ed531a79be5
canonicalized_ast: 675c0542777db276ce7a39decb9dc5aacfde6b00ebfeb3982c480ed531a79be5
type_inferenced_ast: ae8cbfc3938971fc0e42fa400b1277d288ea4b7d4d8149b452fce81ae4813315

View File

@ -1,22 +0,0 @@
---
namespace: Compile
expectation: Pass
outputs:
- circuit:
num_public_variables: 0
num_private_variables: 12
num_constraints: 1
at: 336f487fe39f24aef980deaaf7d6dddcc0dbfa8f121c3470b05546c1ac13f87e
bt: ae35381db5558456a49acb22132b4930efd53b90eb2668df06c5d9c1a6b0ab9f
ct: cf1cbb66a638b4860a516671fb74850e6ccf787fe6c4c8d29e9c04efe880bd05
output:
- input_file: input/dummy.in
output:
registers:
r0:
type: bool
value: "true"
initial_ast: be4b4279f79a35306e1edf5086275a2b216e9d46d66b9bb5fbf650062b7cd263
imports_resolved_ast: c4aae9410df8034a7744ec5f1998454bacde915ddaadfab42181528f1923f742
canonicalized_ast: c4aae9410df8034a7744ec5f1998454bacde915ddaadfab42181528f1923f742
type_inferenced_ast: 0f8434f9e0430dc238602c0479a928be87101566dc0e9ae60ab9f4f1339ef313

View File

@ -1,22 +0,0 @@
---
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: input/dummy.in
output:
registers:
r0:
type: bool
value: "true"
initial_ast: cb9419739db39f806ff96983f53aa085a96238833ed754293042724dd3b29704
imports_resolved_ast: 675a67a8dae0a33a273d74ec021df0e23c5ed7cb32faf8efd2d2f087979de039
canonicalized_ast: b75d02c7cadbcd2906ad0d7b6376bbdc32ff1f26348913e2b0a84ced7496a2f6
type_inferenced_ast: 87ee95068efb0a58e18f2e3b8668db8d2ad9826b21e8b8ce6bc26f60c9fe88ca

View File

@ -1,22 +0,0 @@
---
namespace: Compile
expectation: Pass
outputs:
- circuit:
num_public_variables: 0
num_private_variables: 4
num_constraints: 5
at: e2c343f33b3c986318d821645015951a2f3fb198915bbf602e2c3e58ebfb9c73
bt: 71744948a918814d3008831b4b73101a5cf7346e6ff62d98968eb8b3b91aa343
ct: 94757fb2316d68d18fd26e96a2992b03a8db8d49d802b34201dce6f12518676b
output:
- input_file: input/dummy.in
output:
registers:
r0:
type: bool
value: "true"
initial_ast: 712ed2b7c1ddf180a39cd1bf83c7a4ca3de909a14f87250ec445ba6ae6aa6597
imports_resolved_ast: 36d9e14cf42065047dc21a5c68f45d3264dc0d38eb53355d3f9fef7bd7d512b1
canonicalized_ast: f1edababa6847e2ca24a0786bc1a0b2e66f0d60f5b11c59a24c8b1503f893092
type_inferenced_ast: c979556f787eb3190b0c4cec3d5a6baabbf64d32f7e3281c784f83262da33205

View File

@ -1,68 +0,0 @@
---
namespace: Parse
expectation: Pass
outputs:
- name: ""
expected_input: []
import_statements: []
imports: {}
aliases: {}
circuits: {}
global_consts: {}
functions:
"{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":3,\\\"line_stop\\\":3,\\\"col_start\\\":10,\\\"col_stop\\\":11,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\"function x(x: [u8; _]) {\\\"}\"}":
annotations: {}
identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":3,\\\"line_stop\\\":3,\\\"col_start\\\":10,\\\"col_stop\\\":11,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\"function x(x: [u8; _]) {\\\"}\"}"
input:
- Variable:
identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":3,\\\"line_stop\\\":3,\\\"col_start\\\":12,\\\"col_stop\\\":13,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\"function x(x: [u8; _]) {\\\"}\"}"
const_: false
mutable: true
type_:
Array:
- IntegerType: U8
- - value: "0"
span:
line_start: 3
line_stop: 3
col_start: 12
col_stop: 13
path: ""
content: "function x(x: [u8; _]) {"
const_: false
output: ~
core_mapping: ~
block:
statements:
- Return:
expression:
TupleInit:
elements: []
span:
line_start: 4
line_stop: 4
col_start: 12
col_stop: 14
path: ""
content: " return ();"
span:
line_start: 4
line_stop: 4
col_start: 5
col_stop: 14
path: ""
content: " return ();"
span:
line_start: 3
line_stop: 5
col_start: 24
col_stop: 2
path: ""
content: "function x(x: [u8; _]) {\n ...\n}"
span:
line_start: 3
line_stop: 5
col_start: 1
col_stop: 2
path: ""
content: "function x(x: [u8; _]) {\n ...\n}"

View File

@ -1513,53 +1513,6 @@ outputs:
col_stop: 14
path: ""
content: "let (x,) = ();"
- Definition:
declaration_type: Let
variable_names:
- mutable: true
identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":5,\\\"col_stop\\\":6,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\"let x: [char; _] = \\\\\\\"Hello, World!\\\\\\\";\\\"}\"}"
span:
line_start: 1
line_stop: 1
col_start: 5
col_stop: 6
path: ""
content: "let x: [char; _] = \"Hello, World!\";"
parened: false
type_:
Array:
- Char
- - value: "0"
value:
Value:
String:
- - Scalar: 72
- Scalar: 101
- Scalar: 108
- Scalar: 108
- Scalar: 111
- Scalar: 44
- Scalar: 32
- Scalar: 87
- Scalar: 111
- Scalar: 114
- Scalar: 108
- Scalar: 100
- Scalar: 33
- span:
line_start: 1
line_stop: 1
col_start: 20
col_stop: 35
path: ""
content: "let x: [char; _] = \"Hello, World!\";"
span:
line_start: 1
line_stop: 1
col_start: 1
col_stop: 35
path: ""
content: "let x: [char; _] = \"Hello, World!\";"
- Definition:
declaration_type: Let
variable_names:

View File

@ -1,8 +0,0 @@
/*
namespace: Parse
expectation: Pass
*/
function x(x: [u8; _]) {
return ();
}

View File

@ -99,7 +99,4 @@ let (x,y,) = ();
let (x,) = ();
let x: [char; _] = "Hello, World!";
let x: [[u8; 2]; 2] = [[0,0], [0,0]];