Skip to content

Commit

Permalink
add support for string type items in structs and test it as part of i…
Browse files Browse the repository at this point in the history
…ssue ethereum#343
  • Loading branch information
mjobuda committed Jun 28, 2021
1 parent 6cada6b commit 83b1f53
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 33 deletions.
94 changes: 64 additions & 30 deletions analyzer/src/traversal/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,72 @@ pub fn struct_def(
for field in fields {
let fe::Field { name, typ, .. } = &field.kind;
let field_type = type_desc(&Scope::Module(Rc::clone(&module_scope)), context, typ)?;
if let Type::Base(base_typ) = field_type {
if let Err(AlreadyDefined) = val.add_field(&name.kind, &FixedSize::Base(base_typ)) {
let first_definition = fields
.iter()
.find(|val| {
let fe::Field {
name: inner_name, ..
} = &val.kind;
inner_name.kind == name.kind && val.span != field.span
})
.expect("Missing field");
match field_type {
Type::Base(base_typ) => {
if let Err(AlreadyDefined) = val.add_field(&name.kind, &FixedSize::Base(base_typ)) {
let first_definition = fields
.iter()
.find(|val| {
let fe::Field {
name: inner_name, ..
} = &val.kind;
inner_name.kind == name.kind && val.span != field.span
})
.expect("Missing field");

context.fancy_error(
"a struct field with the same name already exists",
vec![
Label::primary(
first_definition.span,
format!("First definition of field `{}`", name.kind),
),
Label::primary(
field.span,
format!("Conflicting definition of field `{}`", name.kind),
),
],
vec![format!(
"Note: Give one of the `{}` fields a different name",
name.kind
)],
)
context.fancy_error(
"a struct field with the same name already exists",
vec![
Label::primary(
first_definition.span,
format!("First definition of field `{}`", name.kind),
),
Label::primary(
field.span,
format!("Conflicting definition of field `{}`", name.kind),
),
],
vec![format!(
"Note: Give one of the `{}` fields a different name",
name.kind
)],
)
}
}
} else {
context.not_yet_implemented("non-base type struct fields", field.span)
Type::String(string_typ) => {
if let Err(AlreadyDefined) =
val.add_field(&name.kind, &FixedSize::String(string_typ))
{
let first_definition = fields
.iter()
.find(|val| {
let fe::Field {
name: inner_name, ..
} = &val.kind;
inner_name.kind == name.kind && val.span != field.span
})
.expect("Missing field");

context.fancy_error(
"a struct field with the same name already exists",
vec![
Label::primary(
first_definition.span,
format!("First definition of field `{}`", name.kind),
),
Label::primary(
field.span,
format!("Conflicting definition of field `{}`", name.kind),
),
],
vec![format!(
"Note: Give one of the `{}` fields a different name",
name.kind
)],
)
}
}
_ => context.not_yet_implemented("non-base type struct fields", field.span),
}
}
if let Err(AlreadyDefined) = module_scope
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/yul/runtime/functions/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ pub fn generate_get_fn(struct_type: &Struct, field_name: &str) -> yul::Statement
// field, we must take into consideration the left-padding. The left-padding is
// equal to the difference between the value's size and 32 bytes, so we end up
// adding the word offset and the byte offset.
let field_offset = field_index * 32 + (32 - field_type.size());
let field_offset = if field_type.size() < 32 {
field_index * 32 + (32 - field_type.size())
} else {
field_index * field_type.size()
};

let offset = literal_expression! { (field_offset) };
function_definition! {
Expand Down
17 changes: 15 additions & 2 deletions test.fe
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
struct TodoItem:
amount: u256
content: String<100>
contract Foo:
pub def bar():
(42,).abi_encode()
ti1: TodoItem
ti2: TodoItem

pub def set_item():
self.ti1.content = String<100>("Fooooo")
self.ti2.content = String<100>("Fooooo2")

pub def get_number() -> u256:
return self.ti1.amount

pub def get_some_string() -> String<100>:
return self.ti1.content.to_mem()
17 changes: 17 additions & 0 deletions tests/fixtures/features/struct_with_string.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
struct TodoItem:
amount: u256
content: String<100>
contract Foo:
ti1: TodoItem
ti2: TodoItem

pub def set_item():
self.ti1.content = String<100>("Fooooo")
self.ti2.content = String<100>("Fooooo2")

pub def get_number() -> u256:
return self.ti1.amount

pub def get_some_string() -> String<100>:
self.set_item()
return self.ti1.content.to_mem()
14 changes: 14 additions & 0 deletions tests/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,20 @@ fn create_contract_from_init() {
})
}

#[test]
fn struct_with_string() {
with_executor(&|mut executor| {
let string_harness = deploy_contract(&mut executor, "struct_with_string.fe", "Foo", &[]);

string_harness.test_function(
&mut executor,
"get_some_string",
&[],
Some(&string_token("Fooooo")),
);
})
}

#[rstest(
fixture_file,
contract_name,
Expand Down

0 comments on commit 83b1f53

Please sign in to comment.