Skip to content

Commit 341e63e

Browse files
committed
Prevent multiple struct fields with same name
Fixes argotorg#310
1 parent 683428e commit 341e63e

8 files changed

Lines changed: 46 additions & 15 deletions

File tree

analyzer/src/namespace/types.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::errors::SemanticError;
22
use fe_parser::ast as fe;
33
use std::collections::{
4+
btree_map::Entry,
45
BTreeMap,
56
HashMap,
67
};
@@ -210,9 +211,15 @@ impl Struct {
210211
}
211212

212213
/// Add a field to the struct
213-
pub fn add_field(&mut self, name: &str, value: &FixedSize) -> Option<FixedSize> {
214-
self.order.push(name.to_string());
215-
self.fields.insert(name.to_string(), value.clone())
214+
pub fn add_field(&mut self, name: &str, value: &FixedSize) -> Result<(), SemanticError> {
215+
match self.fields.entry(name.to_owned()) {
216+
Entry::Occupied(_) => Err(SemanticError::already_defined()),
217+
Entry::Vacant(entry) => {
218+
entry.insert(value.clone());
219+
self.order.push(name.to_string());
220+
Ok(())
221+
}
222+
}
216223
}
217224

218225
/// Return the type of the given field name

analyzer/src/traversal/structs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn struct_def(
2525
let StructStmt::StructField { name, typ, .. } = &stmt.node;
2626
let field_type = type_desc(&module_scope.borrow().type_defs, &typ.node)?;
2727
if let Type::Base(base_typ) = field_type {
28-
val.add_field(name.node, &FixedSize::Base(base_typ));
28+
val.add_field(name.node, &FixedSize::Base(base_typ))?;
2929
} else {
3030
todo!("Non-Base type fields aren't yet supported")
3131
}

compiler/src/yul/operations/structs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ mod tests {
2828
#[test]
2929
fn test_new() {
3030
let mut val = Struct::new("Foo");
31-
val.add_field("bar", &FixedSize::bool());
32-
val.add_field("bar2", &FixedSize::bool());
31+
val.add_field("bar", &FixedSize::bool()).unwrap();
32+
val.add_field("bar2", &FixedSize::bool()).unwrap();
3333
let params = vec![
3434
identifier_expression! { (1) },
3535
identifier_expression! { (2) },

compiler/src/yul/runtime/functions/structs.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ mod tests {
117117
#[test]
118118
fn test_struct_api_generation() {
119119
let mut val = Struct::new("Foo");
120-
val.add_field("bar", &FixedSize::bool());
121-
val.add_field("bar2", &FixedSize::bool());
120+
val.add_field("bar", &FixedSize::bool()).unwrap();
121+
val.add_field("bar2", &FixedSize::bool()).unwrap();
122122
assert_eq!(
123123
structs::generate_new_fn(&val).to_string(),
124124
"function struct_Foo_new(bar, bar2) -> return_val { return_val := alloc(32) mstore(return_val, bar) let bar2_ptr := alloc(32) mstore(bar2_ptr, bar2) }" )
@@ -127,8 +127,8 @@ mod tests {
127127
#[test]
128128
fn test_struct_getter_generation() {
129129
let mut val = Struct::new("Foo");
130-
val.add_field("bar", &FixedSize::bool());
131-
val.add_field("bar2", &FixedSize::bool());
130+
val.add_field("bar", &FixedSize::bool()).unwrap();
131+
val.add_field("bar2", &FixedSize::bool()).unwrap();
132132
assert_eq!(
133133
structs::generate_get_fn(&val, &val.get_field_names().get(0).unwrap()).to_string(),
134134
"function struct_Foo_get_bar_ptr(ptr) -> return_val { return_val := add(ptr, 31) }"

compiler/tests/compile_errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::fs;
1616
case("duplicate_contract_in_module.fe", "AlreadyDefined"),
1717
case("duplicate_event_in_contract.fe", "AlreadyDefined"),
1818
case("duplicate_field_in_contract.fe", "AlreadyDefined"),
19+
case("duplicate_field_in_struct.fe", "AlreadyDefined"),
1920
case("duplicate_method_in_contract.fe", "AlreadyDefined"),
2021
case("duplicate_struct_in_module.fe", "AlreadyDefined"),
2122
case("duplicate_typedef_in_module.fe", "AlreadyDefined"),
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
struct MyStruct:
2+
foo: u8
3+
foo: u8
4+
5+
contract Foo:
6+
bar: u8

compiler/tests/runtime.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,16 @@ fn test_runtime_set_zero() {
315315
#[test]
316316
fn test_runtime_house_struct() {
317317
let mut house = Struct::new("House");
318-
house.add_field("price", &FixedSize::Base(Base::Numeric(Integer::U256)));
319-
house.add_field("size", &FixedSize::Base(Base::Numeric(Integer::U256)));
320-
house.add_field("rooms", &FixedSize::Base(Base::Numeric(Integer::U8)));
321-
house.add_field("vacant", &FixedSize::bool());
318+
house
319+
.add_field("price", &FixedSize::Base(Base::Numeric(Integer::U256)))
320+
.unwrap();
321+
house
322+
.add_field("size", &FixedSize::Base(Base::Numeric(Integer::U256)))
323+
.unwrap();
324+
house
325+
.add_field("rooms", &FixedSize::Base(Base::Numeric(Integer::U8)))
326+
.unwrap();
327+
house.add_field("vacant", &FixedSize::bool()).unwrap();
322328
let house_api = functions::structs::struct_apis(house);
323329

324330
with_executor(&|mut executor| {

newsfragments/317.bugfix.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,15 @@ contract SomeContract:
2525
2626
contract SomeContract:
2727
other: u8
28-
```
28+
```
29+
30+
31+
Prevent multiple fields with same name in one struct.
32+
33+
Example that now produces a compile time error:
34+
35+
```
36+
struct SomeStruct:
37+
some_field: u8
38+
some_field: u8
39+
```

0 commit comments

Comments
 (0)