Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using a complex type as an array element type #734

Merged
merged 1 commit into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions crates/abi/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,15 @@ impl AbiType {
pub fn header_size(&self) -> usize {
match self {
Self::UInt(_) | Self::Int(_) | Self::Address | Self::Bool | Self::Function => 32,
Self::Array { elem_ty, len } => elem_ty.header_size() * len,
Self::Tuple(fields) => {
if self.is_static() {
fields
.iter()
.fold(0, |acc, field| field.ty.header_size() + acc)
} else {
32
}
}

Self::Array { elem_ty, len } if elem_ty.is_static() => elem_ty.header_size() * len,
Self::Array { .. } => 32,

Self::Tuple(fields) if self.is_static() => fields
.iter()
.fold(0, |acc, field| field.ty.header_size() + acc),
Self::Tuple(_) => 32,

Self::Bytes | Self::String => 32,
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub const U256: Base = Base::Numeric(Integer::U256);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Array {
pub size: usize,
pub inner: Base,
pub inner: Box<Type>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -217,7 +217,7 @@ impl GenericType {
GenericType::Array => vec![
GenericParam {
name: "element type".into(),
kind: GenericParamKind::PrimitiveType,
kind: GenericParamKind::AnyType,
},
GenericParam {
name: "size".into(),
Expand Down Expand Up @@ -246,7 +246,7 @@ impl GenericType {
GenericType::Array => match args {
[GenericArg::Type(element), GenericArg::Int(size)] => Some(Type::Array(Array {
size: *size,
inner: element.as_primitive()?,
inner: Box::new(element.clone()),
})),
_ => None,
},
Expand Down
19 changes: 11 additions & 8 deletions crates/analyzer/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fn index_array(array: Array, index: Type) -> Result<Type, IndexingError> {
return Err(IndexingError::WrongIndexType);
}

Ok(Type::Base(array.inner))
Ok(array.inner.as_ref().clone())
}

fn index_map(map: Map, index: Type) -> Result<Type, IndexingError> {
Expand Down Expand Up @@ -125,13 +125,16 @@ mod tests {
use crate::operations;
use rstest::rstest;

const U256_ARRAY_TYPE: Type = Type::Array(Array {
inner: U256,
size: 100,
});
const U256_TYPE: Type = Type::Base(U256);
const BOOL_TYPE: Type = Type::Base(Base::Bool);

fn u256_array_type() -> Type {
Type::Array(Array {
inner: Box::new(U256.into()),
size: 100,
})
}

fn u256_bool_map() -> Type {
Type::Map(Map {
key: U256,
Expand All @@ -143,7 +146,7 @@ mod tests {
value,
index,
expected,
case(U256_ARRAY_TYPE, U256_TYPE, U256_TYPE),
case(u256_array_type(), U256_TYPE, U256_TYPE),
case(u256_bool_map(), U256_TYPE, BOOL_TYPE)
)]
fn basic_index(value: Type, index: Type, expected: Type) {
Expand All @@ -154,9 +157,9 @@ mod tests {
#[rstest(
value,
index,
case(U256_ARRAY_TYPE, BOOL_TYPE),
case(u256_array_type(), BOOL_TYPE),
case(u256_bool_map(), BOOL_TYPE),
case(u256_bool_map(), U256_ARRAY_TYPE)
case(u256_bool_map(), u256_array_type())
)]
fn type_error_index(value: Type, index: Type) {
let actual = operations::index(value, index).expect_err("didn't fail");
Expand Down
53 changes: 25 additions & 28 deletions crates/analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub fn expr_list(
return Ok(ExpressionAttributes {
typ: Type::Array(Array {
size: 0,
inner: expected_type.map_or(Base::Unit, |arr| arr.inner),
inner: expected_type.map_or(Box::new(Base::Unit.into()), |arr| arr.inner.clone()),
}),
location: Location::Memory,
move_location: None,
Expand All @@ -73,9 +73,8 @@ pub fn expr_list(

let inner_type = if let Some(expected) = expected_type {
for elt in elts {
let element_attributes =
assignable_expr(context, elt, Some(&Type::Base(expected.inner)))?;
if element_attributes.typ != Type::Base(expected.inner) {
let element_attributes = assignable_expr(context, elt, Some(&expected.inner))?;
if &element_attributes.typ != expected.inner.as_ref() {
context.type_error(
"type mismatch",
elt.span,
Expand All @@ -84,11 +83,11 @@ pub fn expr_list(
);
}
}
expected.inner
expected.inner.clone()
} else {
let first_attr = assignable_expr(context, &elts[0], None)?;
let inner = match first_attr.typ {
Type::Base(base) => base,
Type::Base(base) => base.into(),
_ => {
return Err(FatalError::new(context.error(
"arrays can only hold primitive types",
Expand Down Expand Up @@ -119,7 +118,7 @@ pub fn expr_list(
);
}
}
inner
Box::new(inner)
};

// TODO: Right now we are only supporting Base type arrays
Expand Down Expand Up @@ -1011,25 +1010,23 @@ fn expr_call_builtin_function(
expect_no_label_on_arg(context, args, 0);

if let Some(arg_typ) = argument_attributes.first().map(|attr| &attr.typ) {
if !matches!(
arg_typ,
Type::Array(Array {
inner: Base::Numeric(Integer::U8),
..
})
) {
context.fancy_error(
&format!(
"`{}` can not be used as an argument to `{}`",
arg_typ,
function.as_ref(),
),
vec![Label::primary(args.span, "wrong type")],
vec![format!(
"Note: `{}` expects a byte array argument",
function.as_ref()
)],
);
match arg_typ {
Type::Array(Array { inner, .. })
if inner.as_ref() == &Type::Base(Base::Numeric(Integer::U8)) => {}
_ => {
context.fancy_error(
&format!(
"`{}` can not be used as an argument to `{}`",
arg_typ,
function.as_ref(),
),
vec![Label::primary(args.span, "wrong type")],
vec![format!(
"Note: `{}` expects a byte array argument",
function.as_ref()
)],
);
}
}
};
ExpressionAttributes::new(Type::Base(U256), Location::Value)
Expand Down Expand Up @@ -1509,7 +1506,7 @@ fn expr_call_builtin_value_method(
Ok((
ExpressionAttributes::new(
Type::Array(Array {
inner: Base::Numeric(Integer::U8),
inner: Box::new(Base::Numeric(Integer::U8).into()),
size: struct_.id.fields(context.db()).len() * 32,
}),
Location::Memory,
Expand All @@ -1531,7 +1528,7 @@ fn expr_call_builtin_value_method(
Ok((
ExpressionAttributes::new(
Type::Array(Array {
inner: Base::Numeric(Integer::U8),
inner: Box::new(Base::Numeric(Integer::U8).into()),
size: tuple.items.len() * 32,
}),
Location::Memory,
Expand Down
13 changes: 10 additions & 3 deletions crates/analyzer/src/traversal/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn for_loop(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), Fat
// Make sure iter is in the function scope & it should be an array.
let iter_type = expressions::assignable_expr(scope, iter, None)?.typ;
let target_type = if let Type::Array(array) = iter_type {
Type::Base(array.inner)
array.inner
} else {
return Err(FatalError::new(scope.type_error(
"invalid `for` loop iterator type",
Expand All @@ -57,11 +57,18 @@ fn for_loop(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), Fat
)));
};

scope.root.map_variable_type(target, target_type.clone());
scope
.root
.map_variable_type(target, target_type.as_ref().clone());

let mut body_scope = scope.new_child(BlockScopeType::Loop);
// add_var emits a msg on err; we can ignore the Result.
let _ = body_scope.add_var(&target.kind, target_type, false, target.span);
let _ = body_scope.add_var(
&target.kind,
target_type.as_ref().clone(),
false,
target.span,
);

// Traverse the statements within the `for loop` body scope.
traverse_statements(&mut body_scope, body)
Expand Down
1 change: 0 additions & 1 deletion crates/analyzer/tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ macro_rules! test_stmt {
};
}

test_stmt! { array_non_primitive, "let x: Array<(u8, u8), 10>" }
test_stmt! { array_mixed_types, "let x: Array<u16, 3> = [1, address(0), \"hi\"]" }
test_stmt! { array_size_mismatch, "let x: Array<u8, 3> = []\nlet y: Array<u8, 3> = [1, 2]" }
test_stmt! { array_constructor_call, "u8[3]([1, 2, 3])" }
Expand Down
Loading