Merge pull request #1313 from AleoHQ/arrays-without-size

[Implementation] Arrays without size
This commit is contained in:
Alessandro Coglio 2021-09-03 09:21:07 -07:00 committed by GitHub
commit 866066a8cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 258 additions and 63 deletions

View File

@ -91,7 +91,8 @@ impl<'a> FromAst<'a, leo_ast::ArrayAccessExpression> for ArrayAccessExpression<'
Some(PartialType::Array(expected_type.map(Box::new), None)),
)?;
let array_len = match array.get_type() {
Some(Type::Array(_, len)) => len,
Some(Type::Array(_, len)) => Some(len),
Some(Type::ArrayWithoutSize(_)) => None,
type_ => {
return Err(AsgError::unexpected_type(
"array",
@ -113,10 +114,14 @@ impl<'a> FromAst<'a, leo_ast::ArrayAccessExpression> for ArrayAccessExpression<'
.map(|x| x.int().map(|x| x.to_usize()).flatten())
.flatten()
{
if index >= array_len {
return Err(
AsgError::array_index_out_of_bounds(index, &array.span().cloned().unwrap_or_default()).into(),
);
// Only check index if array size is known.
// Array out of bounds will be caught later if it really happens.
if let Some(array_len) = array_len {
if index >= array_len {
return Err(
AsgError::array_index_out_of_bounds(index, &array.span().cloned().unwrap_or_default()).into(),
);
}
}
}

View File

@ -107,6 +107,7 @@ impl<'a> FromAst<'a, leo_ast::ArrayInlineExpression> for ArrayInlineExpression<'
) -> Result<ArrayInlineExpression<'a>> {
let (mut expected_item, expected_len) = match expected_type {
Some(PartialType::Array(item, dims)) => (item.map(|x| *x), dims),
Some(PartialType::Type(Type::ArrayWithoutSize(item))) => (Some(item.partial()), None),
None => (None, None),
Some(type_) => {
return Err(AsgError::unexpected_type(type_, "array", &value.span).into());

View File

@ -183,13 +183,19 @@ impl<'a> Scope<'a> {
IntegerType(int_type) => Type::Integer(int_type.clone()),
Array(sub_type, dimensions) => {
let mut item = Box::new(self.resolve_ast_type(&*sub_type, span)?);
for dimension in dimensions.0.iter().rev() {
let dimension = dimension
.value
.parse::<usize>()
.map_err(|_| AsgError::parse_index_error(span))?;
item = Box::new(Type::Array(item, dimension));
if let Some(dimensions) = dimensions {
for dimension in dimensions.0.iter().rev() {
let dimension = dimension
.value
.parse::<usize>()
.map_err(|_| AsgError::parse_index_error(span))?;
item = Box::new(Type::Array(item, dimension));
}
} else {
item = Box::new(Type::ArrayWithoutSize(item));
}
*item
}
Tuple(sub_types) => Type::Tuple(

View File

@ -32,6 +32,7 @@ pub enum Type<'a> {
// Data type wrappers
Array(Box<Type<'a>>, usize),
ArrayWithoutSize(Box<Type<'a>>),
Tuple(Vec<Type<'a>>),
Circuit(&'a Circuit<'a>),
}
@ -71,6 +72,14 @@ impl<'a> PartialType<'a> {
(PartialType::Integer(self_sub_type, _), Type::Integer(sub_type)) => {
self_sub_type.as_ref().map(|x| x == sub_type).unwrap_or(true)
}
(PartialType::Array(element, _len), Type::ArrayWithoutSize(other_element)) => {
if let Some(element) = element {
if !element.matches(&*other_element) {
return false;
}
}
true
}
(PartialType::Array(element, len), Type::Array(other_element, other_len)) => {
if let Some(element) = element {
if !element.matches(&*other_element) {
@ -114,7 +123,11 @@ impl<'a> Into<PartialType<'a>> for Type<'a> {
impl<'a> Type<'a> {
pub fn is_assignable_from(&self, from: &Type<'a>) -> bool {
self == from
match (self, from) {
(Type::Array(_, _), Type::ArrayWithoutSize(_)) => true,
(Type::ArrayWithoutSize(_), Type::Array(_, _)) => true,
_ => self == from,
}
}
pub fn partial(self) -> PartialType<'a> {
@ -140,6 +153,7 @@ impl<'a> fmt::Display for Type<'a> {
Type::Group => write!(f, "group"),
Type::Integer(sub_type) => sub_type.fmt(f),
Type::Array(sub_type, len) => write!(f, "[{}; {}]", sub_type, len),
Type::ArrayWithoutSize(sub_type) => write!(f, "[{}; _]", sub_type),
Type::Tuple(sub_types) => {
write!(f, "(")?;
for (i, sub_type) in sub_types.iter().enumerate() {
@ -207,10 +221,11 @@ impl<'a> Into<leo_ast::Type> for &Type<'a> {
Integer(int_type) => leo_ast::Type::IntegerType(int_type.clone()),
Array(type_, len) => leo_ast::Type::Array(
Box::new(type_.as_ref().into()),
leo_ast::ArrayDimensions(vec![leo_ast::PositiveNumber {
Some(leo_ast::ArrayDimensions(vec![leo_ast::PositiveNumber {
value: len.to_string().into(),
}]),
}])),
),
ArrayWithoutSize(type_) => leo_ast::Type::Array(Box::new(type_.as_ref().into()), None),
Tuple(subtypes) => leo_ast::Type::Tuple(subtypes.iter().map(Into::into).collect()),
Circuit(circuit) => leo_ast::Type::CircuitOrAlias(circuit.name.borrow().clone()),
}

View File

@ -481,24 +481,31 @@ impl ReconstructingReducer for Canonicalizer {
fn reduce_type(&mut self, _type_: &Type, new: Type, span: &Span) -> Result<Type> {
match new {
Type::Array(type_, mut dimensions) => {
if dimensions.is_zero() {
return Err(AstError::invalid_array_dimension_size(span).into());
}
let mut next = Type::Array(type_, ArrayDimensions(vec![dimensions.remove_last().unwrap()]));
let mut array = next.clone();
loop {
if dimensions.is_empty() {
break;
Type::Array(type_, dimensions) => {
if let Some(mut dimensions) = dimensions {
if dimensions.is_zero() {
return Err(AstError::invalid_array_dimension_size(span).into());
}
array = Type::Array(Box::new(next), ArrayDimensions(vec![dimensions.remove_last().unwrap()]));
next = array.clone();
}
let mut next = Type::Array(type_, Some(ArrayDimensions(vec![dimensions.remove_last().unwrap()])));
let mut array = next.clone();
Ok(array)
loop {
if dimensions.is_empty() {
break;
}
array = Type::Array(
Box::new(next),
Some(ArrayDimensions(vec![dimensions.remove_last().unwrap()])),
);
next = array.clone();
}
Ok(array)
} else {
Ok(Type::Array(type_, None))
}
}
Type::SelfType if !self.in_circuit => Err(AstError::big_self_outside_of_circuit(span).into()),
_ => Ok(new.clone()),

View File

@ -14,12 +14,12 @@
// 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 crate::{ArrayDimensions, Identifier, IntegerType};
use crate::{ArrayDimensions, Identifier, IntegerType, PositiveNumber};
use leo_input::types::{
ArrayType as InputArrayType, DataType as InputDataType, TupleType as InputTupleType, Type as InputType,
};
use serde::{Deserialize, Serialize};
use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer};
use std::fmt;
/// Explicit type used for defining a variable or expression type
@ -34,7 +34,8 @@ pub enum Type {
IntegerType(IntegerType),
// Data type wrappers
Array(Box<Type>, ArrayDimensions),
#[serde(serialize_with = "serialize_array")]
Array(Box<Type>, Option<ArrayDimensions>),
Tuple(Vec<Type>),
CircuitOrAlias(Identifier),
SelfType,
@ -72,8 +73,17 @@ impl Type {
(Type::SelfType, Type::SelfType) => true,
(Type::Array(left_type, left_dim), Type::Array(right_type, right_dim)) => {
// Convert array dimensions to owned.
let mut left_dim_owned = left_dim.to_owned();
let mut right_dim_owned = right_dim.to_owned();
let left_dim_owned = left_dim.to_owned();
let right_dim_owned = right_dim.to_owned();
// Unable to compare arrays with unspecified sizes.
if left_dim_owned.is_none() || right_dim_owned.is_none() {
return false;
}
// We know that values are Some, safe to unwrap.
let mut left_dim_owned = left_dim_owned.unwrap();
let mut right_dim_owned = right_dim_owned.unwrap();
// Remove the first element from both dimensions.
let left_first = left_dim_owned.remove_first();
@ -120,7 +130,7 @@ impl<'ast> From<InputArrayType<'ast>> for Type {
let element_type = Box::new(Type::from(*array_type.type_));
let dimensions = ArrayDimensions::from(array_type.dimensions);
Type::Array(element_type, dimensions)
Type::Array(element_type, Some(dimensions))
}
}
@ -153,7 +163,13 @@ impl fmt::Display for Type {
Type::IntegerType(ref integer_type) => write!(f, "{}", integer_type),
Type::CircuitOrAlias(ref variable) => write!(f, "circuit {}", variable),
Type::SelfType => write!(f, "SelfType"),
Type::Array(ref array, ref dimensions) => write!(f, "[{}; {}]", *array, dimensions),
Type::Array(ref array, ref dimensions) => {
if let Some(dimensions) = dimensions {
write!(f, "[{}; {}]", *array, dimensions)
} else {
write!(f, "[{}; _]", *array)
}
}
Type::Tuple(ref tuple) => {
let types = tuple.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
@ -179,6 +195,25 @@ pub fn inner_array_type(element_type: Type, dimensions: ArrayDimensions) -> Type
element_type
} else {
// The array has multiple dimensions.
Type::Array(Box::new(element_type), dimensions)
Type::Array(Box::new(element_type), Some(dimensions))
}
}
///
/// Custom Serializer for Type::Array. Handles the case when ArrayDimensions are None and turns it into
/// a Vec<PositiveNumber>, where the only element is "0".
///
fn serialize_array<S>(type_: &Type, dimensions: &Option<ArrayDimensions>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(2))?;
seq.serialize_element(type_)?;
// seq.serialize_element(dimensions)?;
if let Some(dimensions) = dimensions {
seq.serialize_element(&dimensions)?;
} else {
seq.serialize_element(&ArrayDimensions(vec![PositiveNumber { value: "0".into() }]))?;
}
seq.end()
}

View File

@ -76,9 +76,9 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
if self.options.type_inference_enabled() {
AstType::Array(
Box::new(self.reduce_type(ast_type, asg_type, span)?),
ArrayDimensions(vec![PositiveNumber {
Some(ArrayDimensions(vec![PositiveNumber {
value: StrTendril::from(format!("{}", asg_dimensions)),
}]),
}])),
)
} else {
AstType::Array(

View File

@ -32,7 +32,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an invalid address literal.
/// For when the parser encountered an invalid address literal.
@formatted
invalid_address_lit {
args: (token: impl Display),
@ -40,7 +40,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an empty import list.
/// For when the parser encountered an empty import list.
@formatted
invalid_import_list {
args: (),
@ -48,7 +48,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an unexpected End of File.
/// For when the parser encountered an unexpected End of File.
@formatted
unexpected_eof {
args: (),
@ -56,7 +56,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an unexpected whitespace.
/// For when the parser encountered an unexpected whitespace.
@formatted
unexpected_whitespace {
args: (left: impl Display, right: impl Display),
@ -64,7 +64,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an unexpected list of tokens.
/// For when the parser encountered an unexpected list of tokens.
@formatted
unexpected {
args: (got: impl Display, expected: impl Display),
@ -72,7 +72,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered a mix of commas and semi-colons in circuit member variables.
/// For when the parser encountered a mix of commas and semi-colons in circuit member variables.
@formatted
mixed_commas_and_semicolons {
args: (),
@ -80,7 +80,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an unexpected identifier.
/// For when the parser encountered an unexpected identifier.
@formatted
unexpected_ident {
args: (got: impl Display, expected: &[impl Display]),
@ -96,7 +96,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an unexpected statement.
/// For when the parser encountered an unexpected statement.
@formatted
unexpected_statement {
args: (got: impl Display, expected: impl Display),
@ -104,7 +104,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an unexpected string.
/// For when the parser encountered an unexpected string.
@formatted
unexpected_str {
args: (got: impl Display, expected: impl Display),
@ -112,7 +112,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an unexpected spread in an array init expression.
/// For when the parser encountered an unexpected spread in an array init expression.
@formatted
spread_in_array_init {
args: (),
@ -120,7 +120,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an invalid assignment target.
/// For when the parser encountered an invalid assignment target.
@formatted
invalid_assignment_target {
args: (),
@ -128,7 +128,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an invalid package name.
/// For when the parser encountered an invalid package name.
@formatted
invalid_package_name {
args: (),
@ -136,7 +136,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered an illegal `const self` argument.
/// For when the parser encountered an illegal `const self` argument.
@formatted
illegal_self_const {
args: (),
@ -144,7 +144,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered a deprecated `mut` argument in a function.
/// For when the parser encountered a deprecated `mut` argument in a function.
@formatted
mut_function_input {
args: (),
@ -152,7 +152,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered a deprecated `mut` argument in a let statement.
/// For when the parser encountered a deprecated `mut` argument in a let statement.
@formatted
let_mut_statement {
args: (),
@ -160,7 +160,7 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered a deprecated `test function`.
/// For when the parser encountered a deprecated `test function`.
@formatted
test_function {
args: (),
@ -168,11 +168,19 @@ create_errors!(
help: None,
}
/// For when the parser encoutnered a deprecated `@context(...)` annotation.
/// For when the parser encountered a deprecated `@context(...)` annotation.
@formatted
context_annotation {
args: (),
msg: "\"@context(...)\" is deprecated. Did you mean @test annotation?",
help: None,
}
/// For when the parser failed to parse array dimensions.
@formatted
unable_to_parse_array_dimensions {
args: (),
msg: "unable to parse array dimensions",
help: None,
}
);

View File

@ -678,8 +678,10 @@ tuple-type = "(" [ type 1*( "," type ) ] ")"
array-type = "[" type ";" array-dimensions "]"
array-dimensions = natural
/ "(" natural *( "," natural ) ")"
array-dimension = natural / "_"
array-dimensions = array-dimension
/ "(" array-dimension *( "," array-dimension ) ")"
; Scalar and the remaining types form all the types.

View File

@ -604,7 +604,9 @@ impl ParserContext {
}
let first = self.parse_spread_or_expression()?;
if self.eat(Token::Semicolon).is_some() {
let dimensions = self.parse_array_dimensions()?;
let dimensions = self
.parse_array_dimensions()?
.ok_or_else(|| ParserError::unable_to_parse_array_dimensions(span))?;
let end = self.expect(Token::RightSquare)?;
let first = match first {
SpreadOrExpression::Spread(first) => {

View File

@ -58,9 +58,11 @@ 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> {
pub fn parse_array_dimensions(&mut self) -> Result<Option<ArrayDimensions>> {
Ok(if let Some((int, _)) = self.eat_int() {
ArrayDimensions(vec![int])
Some(ArrayDimensions(vec![int]))
} else if self.eat(Token::Underscore).is_some() {
None
} else {
self.expect(Token::LeftParen)?;
let mut dimensions = Vec::new();
@ -76,7 +78,7 @@ impl ParserContext {
}
}
self.expect(Token::RightParen)?;
ArrayDimensions(dimensions)
Some(ArrayDimensions(dimensions))
})
}

View File

@ -20,4 +20,4 @@ function main(y: bool) -> bool {
let b: [int; 3] = return_int_array();
return y;
}
}

View File

@ -0,0 +1,11 @@
/*
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

@ -0,0 +1,13 @@
/*
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

@ -0,0 +1,6 @@
[main]
y: bool = true;
n: bool = false;
[registers]
r0: bool = false;

View File

@ -0,0 +1,16 @@
/*
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

@ -0,0 +1,22 @@
---
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: edf8a3b7372af353b99830752d41d8c04d1863a4d03c754f41aac3545649c644
imports_resolved_ast: edf8a3b7372af353b99830752d41d8c04d1863a4d03c754f41aac3545649c644
canonicalized_ast: edf8a3b7372af353b99830752d41d8c04d1863a4d03c754f41aac3545649c644
type_inferenced_ast: 5479f110a1cbd68040560f64a09663207e756630aa2621a4bb424c48a3cab64d

View File

@ -0,0 +1,22 @@
---
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: 5ae730ffb3671acde08944aaa8450a54bb9ce436c92d5c21e7a2a7b9c8d404a7
imports_resolved_ast: 5ae730ffb3671acde08944aaa8450a54bb9ce436c92d5c21e7a2a7b9c8d404a7
canonicalized_ast: 5ae730ffb3671acde08944aaa8450a54bb9ce436c92d5c21e7a2a7b9c8d404a7
type_inferenced_ast: c2b15c1e0644a4af597019f7a56d1c8485723e43089ff0aa8951e3ec31729f1f

View File

@ -0,0 +1,22 @@
---
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: 1695abb58931812bfe65ffdb967c9e8e36abbab771bfd8a20e289e3e1b102b5a
imports_resolved_ast: 1695abb58931812bfe65ffdb967c9e8e36abbab771bfd8a20e289e3e1b102b5a
canonicalized_ast: 637eaabe62c318b0c9f9d6d26936c11aa8804022866ce356e14dc02e29a34251
type_inferenced_ast: 1540899195a176d1b1d0e1d886a671f07f063dd10fea15f853e67ffcfc0ae9ce