diff --git a/crates/analyzer/src/traversal/expressions.rs b/crates/analyzer/src/traversal/expressions.rs index a8751d647e..b4e77424c1 100644 --- a/crates/analyzer/src/traversal/expressions.rs +++ b/crates/analyzer/src/traversal/expressions.rs @@ -74,19 +74,24 @@ pub fn expr_list( let inner_type = if let Some(expected) = expected_type { for elt in elts { - let attr = expr( + let element_attributes = assignable_expr( Rc::clone(&scope), context, elt, Some(&Type::Base(expected.inner)), )?; - if attr.typ != Type::Base(expected.inner) { - context.type_error("type mismatch", elt.span, &expected.inner, attr.typ); + if element_attributes.typ != Type::Base(expected.inner) { + context.type_error( + "type mismatch", + elt.span, + &expected.inner, + element_attributes.typ, + ); } } expected.inner } else { - let first_attr = expr(Rc::clone(&scope), context, &elts[0], None)?; + let first_attr = assignable_expr(Rc::clone(&scope), context, &elts[0], None)?; let inner = match first_attr.typ { Type::Base(base) => base, _ => { @@ -105,13 +110,17 @@ pub fn expr_list( // Assuming every element attribute should match the attribute of 0th element // of list. for elt in &elts[1..] { - let attr = expr(Rc::clone(&scope), context, elt, Some(&first_attr.typ))?; - if attr.typ != first_attr.typ { + let element_attributes = + assignable_expr(Rc::clone(&scope), context, elt, Some(&first_attr.typ))?; + if element_attributes.typ != first_attr.typ { context.fancy_error( "array elements must have same type", vec![ Label::primary(elts[0].span, format!("this has type `{}`", first_attr.typ)), - Label::secondary(elt.span, format!("this has type `{}`", attr.typ)), + Label::secondary( + elt.span, + format!("this has type `{}`", element_attributes.typ), + ), ], vec![], ); @@ -121,7 +130,7 @@ pub fn expr_list( }; // TODO: Right now we are only supporting Base type arrays - // Potential we can support the tuples as well. + // Potentially we can support tuples as well. let array_typ = Array { size: elts.len(), inner: inner_type, diff --git a/crates/analyzer/src/traversal/functions.rs b/crates/analyzer/src/traversal/functions.rs index dfa76b5a53..062b4d10ad 100644 --- a/crates/analyzer/src/traversal/functions.rs +++ b/crates/analyzer/src/traversal/functions.rs @@ -250,9 +250,10 @@ fn for_loop( fe::FuncStmt::For { target, iter, body } => { // Create the for loop body scope. let body_scope = BlockScope::from_block_scope(BlockScopeType::Loop, Rc::clone(&scope)); - // Make sure iter is in the function scope & it should be an array. - let iter_type = expressions::expr(Rc::clone(&scope), context, &iter, None)?.typ; + // Make sure iter is in the function scope and it should be an array. + let iter_type = + expressions::assignable_expr(Rc::clone(&scope), context, &iter, None)?.typ; let target_type = if let Type::Array(array) = iter_type { FixedSize::Base(array.inner) } else { @@ -264,8 +265,7 @@ fn for_loop( ); return Err(FatalError::new()); }; - if let Err(AlreadyDefined) = body_scope.borrow_mut().add_var(&target.kind, target_type) - { + if let Err(_) = body_scope.borrow_mut().add_var(&target.kind, target_type) { context.fancy_error( "a variable with the same name already exists in this scope", // TODO: figure out how to include the previously defined var @@ -317,7 +317,7 @@ fn if_statement( body, or_else, } => { - let test_type = expressions::expr(Rc::clone(&scope), context, test, None)?.typ; + let test_type = expressions::value_expr(Rc::clone(&scope), context, test, None)?.typ; if test_type != Type::Base(Base::Bool) { context.type_error( "`if` statement condition is not bool", @@ -346,7 +346,7 @@ fn while_loop( ) -> Result<(), FatalError> { match &stmt.kind { fe::FuncStmt::While { test, body } => { - let test_type = expressions::expr(Rc::clone(&scope), context, &test, None)?.typ; + let test_type = expressions::value_expr(Rc::clone(&scope), context, &test, None)?.typ; if test_type != Type::Base(Base::Bool) { context.type_error( "`while` loop condition is not bool", @@ -412,7 +412,7 @@ fn assert( stmt: &Node, ) -> Result<(), FatalError> { if let fe::FuncStmt::Assert { test, msg } = &stmt.kind { - let test_type = expressions::expr(Rc::clone(&scope), context, &test, None)?.typ; + let test_type = expressions::value_expr(Rc::clone(&scope), context, &test, None)?.typ; if test_type != Type::Base(Base::Bool) { context.type_error( "`assert` condition is not bool", @@ -423,7 +423,7 @@ fn assert( } if let Some(msg) = msg { - let msg_attributes = expressions::expr(scope, context, msg, None)?; + let msg_attributes = expressions::assignable_expr(scope, context, msg, None)?; if !matches!(msg_attributes.typ, Type::String(_)) { context.error( "`assert` reason must be a string", @@ -446,7 +446,8 @@ fn revert( ) -> Result<(), FatalError> { if let fe::FuncStmt::Revert { error } = &stmt.kind { if let Some(error_expr) = error { - let error_attributes = expressions::expr(Rc::clone(&scope), context, error_expr, None)?; + let error_attributes = + expressions::assignable_expr(Rc::clone(&scope), context, error_expr, None)?; if !matches!(error_attributes.typ, Type::Struct(_)) { context.error( "`revert` error must be a struct", diff --git a/crates/analyzer/tests/errors.rs b/crates/analyzer/tests/errors.rs index ad0a5fd896..e26735421e 100644 --- a/crates/analyzer/tests/errors.rs +++ b/crates/analyzer/tests/errors.rs @@ -197,3 +197,6 @@ test_file! { struct_call_without_kw_args } test_file! { non_pub_init } test_file! { abi_encode_u256 } test_file! { abi_encode_from_storage } +test_file! { assert_sto_msg_no_copy } +test_file! { for_loop_sto_iter_no_copy } +test_file! { revert_sto_error_no_copy } diff --git a/crates/analyzer/tests/snapshots/analysis__assert.snap b/crates/analyzer/tests/snapshots/analysis__assert.snap index 4bed33a044..98555a2da9 100644 --- a/crates/analyzer/tests/snapshots/analysis__assert.snap +++ b/crates/analyzer/tests/snapshots/analysis__assert.snap @@ -9,6 +9,22 @@ ModuleAttributes { Contract { name: "Foo", functions: [ + FunctionAttributes { + is_public: true, + name: "assert_sto_bool", + params: [], + return_type: Base( + Unit, + ), + }, + FunctionAttributes { + is_public: true, + name: "assert_sto_string_msg", + params: [], + return_type: Base( + Unit, + ), + }, FunctionAttributes { is_public: true, name: "bar", @@ -75,9 +91,9 @@ ModuleAttributes { } note: - ┌─ features/assert.fe:3:16 + ┌─ features/assert.fe:6:16 │ -3 │ assert baz > 5 +6 │ assert baz > 5 │ ^^^ attributes hash: 1230752710897721197 │ = ExpressionAttributes { @@ -91,9 +107,9 @@ note: } note: - ┌─ features/assert.fe:3:22 + ┌─ features/assert.fe:6:22 │ -3 │ assert baz > 5 +6 │ assert baz > 5 │ ^ attributes hash: 1230752710897721197 │ = ExpressionAttributes { @@ -107,9 +123,9 @@ note: } note: - ┌─ features/assert.fe:3:16 + ┌─ features/assert.fe:6:16 │ -3 │ assert baz > 5 +6 │ assert baz > 5 │ ^^^^^^^ attributes hash: 519621297275845584 │ = ExpressionAttributes { @@ -121,9 +137,9 @@ note: } note: - ┌─ features/assert.fe:6:16 + ┌─ features/assert.fe:9:16 │ -6 │ assert baz > 5, "Must be greater than five" +9 │ assert baz > 5, "Must be greater than five" │ ^^^ attributes hash: 1230752710897721197 │ = ExpressionAttributes { @@ -137,9 +153,9 @@ note: } note: - ┌─ features/assert.fe:6:22 + ┌─ features/assert.fe:9:22 │ -6 │ assert baz > 5, "Must be greater than five" +9 │ assert baz > 5, "Must be greater than five" │ ^ attributes hash: 1230752710897721197 │ = ExpressionAttributes { @@ -153,9 +169,9 @@ note: } note: - ┌─ features/assert.fe:6:16 + ┌─ features/assert.fe:9:16 │ -6 │ assert baz > 5, "Must be greater than five" +9 │ assert baz > 5, "Must be greater than five" │ ^^^^^^^ attributes hash: 519621297275845584 │ = ExpressionAttributes { @@ -167,9 +183,9 @@ note: } note: - ┌─ features/assert.fe:6:25 + ┌─ features/assert.fe:9:25 │ -6 │ assert baz > 5, "Must be greater than five" +9 │ assert baz > 5, "Must be greater than five" │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 16264676341784907870 │ = ExpressionAttributes { @@ -183,72 +199,216 @@ note: } note: - ┌─ features/assert.fe:9:16 - │ -9 │ assert baz > 5, reason - │ ^^^ attributes hash: 1230752710897721197 - │ - = ExpressionAttributes { - typ: Base( - Numeric( - U256, - ), - ), - location: Value, - move_location: None, - } + ┌─ features/assert.fe:12:16 + │ +12 │ assert baz > 5, reason + │ ^^^ attributes hash: 1230752710897721197 + │ + = ExpressionAttributes { + typ: Base( + Numeric( + U256, + ), + ), + location: Value, + move_location: None, + } note: - ┌─ features/assert.fe:9:22 - │ -9 │ assert baz > 5, reason - │ ^ attributes hash: 1230752710897721197 - │ - = ExpressionAttributes { - typ: Base( - Numeric( - U256, - ), - ), - location: Value, - move_location: None, - } + ┌─ features/assert.fe:12:22 + │ +12 │ assert baz > 5, reason + │ ^ attributes hash: 1230752710897721197 + │ + = ExpressionAttributes { + typ: Base( + Numeric( + U256, + ), + ), + location: Value, + move_location: None, + } note: - ┌─ features/assert.fe:9:16 - │ -9 │ assert baz > 5, reason - │ ^^^^^^^ attributes hash: 519621297275845584 - │ - = ExpressionAttributes { - typ: Base( - Bool, - ), - location: Value, - move_location: None, - } + ┌─ features/assert.fe:12:16 + │ +12 │ assert baz > 5, reason + │ ^^^^^^^ attributes hash: 519621297275845584 + │ + = ExpressionAttributes { + typ: Base( + Bool, + ), + location: Value, + move_location: None, + } note: - ┌─ features/assert.fe:9:25 - │ -9 │ assert baz > 5, reason - │ ^^^^^^ attributes hash: 3599122874764053339 - │ - = ExpressionAttributes { - typ: String( - FeString { - max_size: 1000, - }, - ), - location: Memory, - move_location: None, - } + ┌─ features/assert.fe:12:25 + │ +12 │ assert baz > 5, reason + │ ^^^^^^ attributes hash: 3599122874764053339 + │ + = ExpressionAttributes { + typ: String( + FeString { + max_size: 1000, + }, + ), + location: Memory, + move_location: None, + } + +note: + ┌─ features/assert.fe:15:9 + │ +15 │ self.my_bool = false + │ ^^^^^^^^^^^^ attributes hash: 12273323640399429874 + │ + = ExpressionAttributes { + typ: Base( + Bool, + ), + location: Storage { + nonce: Some( + 0, + ), + }, + move_location: None, + } + +note: + ┌─ features/assert.fe:15:24 + │ +15 │ self.my_bool = false + │ ^^^^^ attributes hash: 519621297275845584 + │ + = ExpressionAttributes { + typ: Base( + Bool, + ), + location: Value, + move_location: None, + } + +note: + ┌─ features/assert.fe:16:16 + │ +16 │ assert self.my_bool + │ ^^^^^^^^^^^^ attributes hash: 13730979586018155653 + │ + = ExpressionAttributes { + typ: Base( + Bool, + ), + location: Storage { + nonce: Some( + 0, + ), + }, + move_location: Some( + Value, + ), + } + +note: + ┌─ features/assert.fe:19:9 + │ +19 │ self.my_string = "hello" + │ ^^^^^^^^^^^^^^ attributes hash: 9933875619792709288 + │ + = ExpressionAttributes { + typ: String( + FeString { + max_size: 5, + }, + ), + location: Storage { + nonce: Some( + 1, + ), + }, + move_location: None, + } + +note: + ┌─ features/assert.fe:19:26 + │ +19 │ self.my_string = "hello" + │ ^^^^^^^ attributes hash: 4232494716478429185 + │ + = ExpressionAttributes { + typ: String( + FeString { + max_size: 5, + }, + ), + location: Memory, + move_location: None, + } + +note: + ┌─ features/assert.fe:20:16 + │ +20 │ assert false, self.my_string.to_mem() + │ ^^^^^ attributes hash: 519621297275845584 + │ + = ExpressionAttributes { + typ: Base( + Bool, + ), + location: Value, + move_location: None, + } + +note: + ┌─ features/assert.fe:20:23 + │ +20 │ assert false, self.my_string.to_mem() + │ ^^^^^^^^^^^^^^ attributes hash: 9933875619792709288 + │ + = ExpressionAttributes { + typ: String( + FeString { + max_size: 5, + }, + ), + location: Storage { + nonce: Some( + 1, + ), + }, + move_location: None, + } + +note: + ┌─ features/assert.fe:20:23 + │ +20 │ assert false, self.my_string.to_mem() + │ ^^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 1560482948859588432 + │ + = ExpressionAttributes { + typ: String( + FeString { + max_size: 5, + }, + ), + location: Storage { + nonce: Some( + 1, + ), + }, + move_location: Some( + Memory, + ), + } note: - ┌─ features/assert.fe:2:5 + ┌─ features/assert.fe:5:5 │ -2 │ ╭ pub def bar(baz: u256): -3 │ │ assert baz > 5 +5 │ ╭ pub def bar(baz: u256): +6 │ │ assert baz > 5 │ ╰──────────────────────^ attributes hash: 14705602729846184909 │ = FunctionAttributes { @@ -270,10 +430,10 @@ note: } note: - ┌─ features/assert.fe:5:5 + ┌─ features/assert.fe:8:5 │ -5 │ ╭ pub def revert_with_static_string(baz: u256): -6 │ │ assert baz > 5, "Must be greater than five" +8 │ ╭ pub def revert_with_static_string(baz: u256): +9 │ │ assert baz > 5, "Must be greater than five" │ ╰───────────────────────────────────────────────────^ attributes hash: 12915933724383760692 │ = FunctionAttributes { @@ -295,122 +455,180 @@ note: } note: - ┌─ features/assert.fe:8:5 - │ -8 │ ╭ pub def revert_with(baz: u256, reason: String<1000>): -9 │ │ assert baz > 5, reason - │ ╰──────────────────────────────^ attributes hash: 5206967184411358358 - │ - = FunctionAttributes { - is_public: true, - name: "revert_with", - params: [ - ( - "baz", - Base( - Numeric( - U256, - ), - ), - ), - ( - "reason", - String( - FeString { - max_size: 1000, - }, - ), - ), - ], - return_type: Base( - Unit, - ), - } + ┌─ features/assert.fe:11:5 + │ +11 │ ╭ pub def revert_with(baz: u256, reason: String<1000>): +12 │ │ assert baz > 5, reason + │ ╰──────────────────────────────^ attributes hash: 5206967184411358358 + │ + = FunctionAttributes { + is_public: true, + name: "revert_with", + params: [ + ( + "baz", + Base( + Numeric( + U256, + ), + ), + ), + ( + "reason", + String( + FeString { + max_size: 1000, + }, + ), + ), + ], + return_type: Base( + Unit, + ), + } note: - ┌─ features/assert.fe:1:1 - │ -1 │ ╭ contract Foo: -2 │ │ pub def bar(baz: u256): -3 │ │ assert baz > 5 -4 │ │ - · │ -8 │ │ pub def revert_with(baz: u256, reason: String<1000>): -9 │ │ assert baz > 5, reason - │ ╰──────────────────────────────^ attributes hash: 2501283307939429907 - │ - = ContractAttributes { - public_functions: [ - FunctionAttributes { - is_public: true, - name: "bar", - params: [ - ( - "baz", - Base( - Numeric( - U256, - ), - ), - ), - ], - return_type: Base( - Unit, - ), - }, - FunctionAttributes { - is_public: true, - name: "revert_with", - params: [ - ( - "baz", - Base( - Numeric( - U256, - ), - ), - ), - ( - "reason", - String( - FeString { - max_size: 1000, - }, - ), - ), - ], - return_type: Base( - Unit, - ), - }, - FunctionAttributes { - is_public: true, - name: "revert_with_static_string", - params: [ - ( - "baz", - Base( - Numeric( - U256, - ), - ), - ), - ], - return_type: Base( - Unit, - ), - }, - ], - init_function: None, - events: [], - structs: [], - external_contracts: [], - } + ┌─ features/assert.fe:14:5 + │ +14 │ ╭ pub def assert_sto_bool(): +15 │ │ self.my_bool = false +16 │ │ assert self.my_bool + │ ╰───────────────────────────^ attributes hash: 386062522787178436 + │ + = FunctionAttributes { + is_public: true, + name: "assert_sto_bool", + params: [], + return_type: Base( + Unit, + ), + } + +note: + ┌─ features/assert.fe:18:5 + │ +18 │ ╭ pub def assert_sto_string_msg(): +19 │ │ self.my_string = "hello" +20 │ │ assert false, self.my_string.to_mem() + │ ╰─────────────────────────────────────────────^ attributes hash: 1836150226267414980 + │ + = FunctionAttributes { + is_public: true, + name: "assert_sto_string_msg", + params: [], + return_type: Base( + Unit, + ), + } note: - ┌─ features/assert.fe:2:22 + ┌─ features/assert.fe:1:1 + │ + 1 │ ╭ contract Foo: + 2 │ │ my_bool: bool + 3 │ │ my_string: String<5> + 4 │ │ + · │ +19 │ │ self.my_string = "hello" +20 │ │ assert false, self.my_string.to_mem() + │ ╰─────────────────────────────────────────────^ attributes hash: 1872320294411724862 + │ + = ContractAttributes { + public_functions: [ + FunctionAttributes { + is_public: true, + name: "assert_sto_bool", + params: [], + return_type: Base( + Unit, + ), + }, + FunctionAttributes { + is_public: true, + name: "assert_sto_string_msg", + params: [], + return_type: Base( + Unit, + ), + }, + FunctionAttributes { + is_public: true, + name: "bar", + params: [ + ( + "baz", + Base( + Numeric( + U256, + ), + ), + ), + ], + return_type: Base( + Unit, + ), + }, + FunctionAttributes { + is_public: true, + name: "revert_with", + params: [ + ( + "baz", + Base( + Numeric( + U256, + ), + ), + ), + ( + "reason", + String( + FeString { + max_size: 1000, + }, + ), + ), + ], + return_type: Base( + Unit, + ), + }, + FunctionAttributes { + is_public: true, + name: "revert_with_static_string", + params: [ + ( + "baz", + Base( + Numeric( + U256, + ), + ), + ), + ], + return_type: Base( + Unit, + ), + }, + ], + init_function: None, + events: [], + structs: [], + external_contracts: [], + } + +note: + ┌─ features/assert.fe:20:23 + │ +20 │ assert false, self.my_string.to_mem() + │ ^^^^^^^^^^^^^^^^^^^^^ attributes hash: 15856680294290209687 + │ + = ValueAttribute + +note: + ┌─ features/assert.fe:5:22 │ -2 │ pub def bar(baz: u256): +5 │ pub def bar(baz: u256): │ ^^^^ attributes hash: 4293763436908729629 │ = Base( @@ -420,9 +638,9 @@ note: ) note: - ┌─ features/assert.fe:5:44 + ┌─ features/assert.fe:8:44 │ -5 │ pub def revert_with_static_string(baz: u256): +8 │ pub def revert_with_static_string(baz: u256): │ ^^^^ attributes hash: 4293763436908729629 │ = Base( @@ -432,26 +650,48 @@ note: ) note: - ┌─ features/assert.fe:8:30 + ┌─ features/assert.fe:11:30 + │ +11 │ pub def revert_with(baz: u256, reason: String<1000>): + │ ^^^^ attributes hash: 4293763436908729629 + │ + = Base( + Numeric( + U256, + ), + ) + +note: + ┌─ features/assert.fe:11:44 + │ +11 │ pub def revert_with(baz: u256, reason: String<1000>): + │ ^^^^^^^^^^^^ attributes hash: 7691650642384229431 + │ + = String( + FeString { + max_size: 1000, + }, + ) + +note: + ┌─ features/assert.fe:2:14 │ -8 │ pub def revert_with(baz: u256, reason: String<1000>): - │ ^^^^ attributes hash: 4293763436908729629 +2 │ my_bool: bool + │ ^^^^ attributes hash: 5425972608982369985 │ = Base( - Numeric( - U256, - ), + Bool, ) note: - ┌─ features/assert.fe:8:44 + ┌─ features/assert.fe:3:16 │ -8 │ pub def revert_with(baz: u256, reason: String<1000>): - │ ^^^^^^^^^^^^ attributes hash: 7691650642384229431 +3 │ my_string: String<5> + │ ^^^^^^^^^ attributes hash: 4442886494745637937 │ = String( FeString { - max_size: 1000, + max_size: 5, }, ) diff --git a/crates/analyzer/tests/snapshots/analysis__revert.snap b/crates/analyzer/tests/snapshots/analysis__revert.snap index 97102b8546..a33ffae764 100644 --- a/crates/analyzer/tests/snapshots/analysis__revert.snap +++ b/crates/analyzer/tests/snapshots/analysis__revert.snap @@ -56,6 +56,14 @@ ModuleAttributes { Unit, ), }, + FunctionAttributes { + is_public: true, + name: "revert_other_error_from_sto", + params: [], + return_type: Base( + Unit, + ), + }, ], }, ), @@ -84,9 +92,9 @@ ModuleAttributes { } note: - ┌─ features/revert.fe:14:26 + ┌─ features/revert.fe:16:26 │ -14 │ revert Error(msg=1, val=true) +16 │ revert Error(msg=1, val=true) │ ^ attributes hash: 1230752710897721197 │ = ExpressionAttributes { @@ -100,9 +108,9 @@ note: } note: - ┌─ features/revert.fe:14:33 + ┌─ features/revert.fe:16:33 │ -14 │ revert Error(msg=1, val=true) +16 │ revert Error(msg=1, val=true) │ ^^^^ attributes hash: 519621297275845584 │ = ExpressionAttributes { @@ -114,9 +122,9 @@ note: } note: - ┌─ features/revert.fe:14:16 + ┌─ features/revert.fe:16:16 │ -14 │ revert Error(msg=1, val=true) +16 │ revert Error(msg=1, val=true) │ ^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 11694781548607974207 │ = ExpressionAttributes { @@ -146,9 +154,9 @@ note: } note: - ┌─ features/revert.fe:17:31 + ┌─ features/revert.fe:19:31 │ -17 │ revert OtherError(msg=1, val=true) +19 │ revert OtherError(msg=1, val=true) │ ^ attributes hash: 1230752710897721197 │ = ExpressionAttributes { @@ -162,9 +170,9 @@ note: } note: - ┌─ features/revert.fe:17:38 + ┌─ features/revert.fe:19:38 │ -17 │ revert OtherError(msg=1, val=true) +19 │ revert OtherError(msg=1, val=true) │ ^^^^ attributes hash: 519621297275845584 │ = ExpressionAttributes { @@ -176,9 +184,9 @@ note: } note: - ┌─ features/revert.fe:17:16 + ┌─ features/revert.fe:19:16 │ -17 │ revert OtherError(msg=1, val=true) +19 │ revert OtherError(msg=1, val=true) │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 6207322651044042545 │ = ExpressionAttributes { @@ -208,10 +216,182 @@ note: } note: - ┌─ features/revert.fe:10:5 + ┌─ features/revert.fe:22:9 + │ +22 │ self.my_other_error = OtherError(msg=1, val=true) + │ ^^^^^^^^^^^^^^^^^^^ attributes hash: 1455945768619151908 + │ + = ExpressionAttributes { + typ: Struct( + Struct { + name: "OtherError", + fields: [ + ( + "msg", + Base( + Numeric( + U256, + ), + ), + ), + ( + "val", + Base( + Bool, + ), + ), + ], + }, + ), + location: Storage { + nonce: Some( + 0, + ), + }, + move_location: None, + } + +note: + ┌─ features/revert.fe:22:46 + │ +22 │ self.my_other_error = OtherError(msg=1, val=true) + │ ^ attributes hash: 1230752710897721197 + │ + = ExpressionAttributes { + typ: Base( + Numeric( + U256, + ), + ), + location: Value, + move_location: None, + } + +note: + ┌─ features/revert.fe:22:53 + │ +22 │ self.my_other_error = OtherError(msg=1, val=true) + │ ^^^^ attributes hash: 519621297275845584 + │ + = ExpressionAttributes { + typ: Base( + Bool, + ), + location: Value, + move_location: None, + } + +note: + ┌─ features/revert.fe:22:31 + │ +22 │ self.my_other_error = OtherError(msg=1, val=true) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 6207322651044042545 + │ + = ExpressionAttributes { + typ: Struct( + Struct { + name: "OtherError", + fields: [ + ( + "msg", + Base( + Numeric( + U256, + ), + ), + ), + ( + "val", + Base( + Bool, + ), + ), + ], + }, + ), + location: Memory, + move_location: None, + } + +note: + ┌─ features/revert.fe:23:16 + │ +23 │ revert self.my_other_error.to_mem() + │ ^^^^^^^^^^^^^^^^^^^ attributes hash: 1455945768619151908 + │ + = ExpressionAttributes { + typ: Struct( + Struct { + name: "OtherError", + fields: [ + ( + "msg", + Base( + Numeric( + U256, + ), + ), + ), + ( + "val", + Base( + Bool, + ), + ), + ], + }, + ), + location: Storage { + nonce: Some( + 0, + ), + }, + move_location: None, + } + +note: + ┌─ features/revert.fe:23:16 + │ +23 │ revert self.my_other_error.to_mem() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 4295708869972799250 + │ + = ExpressionAttributes { + typ: Struct( + Struct { + name: "OtherError", + fields: [ + ( + "msg", + Base( + Numeric( + U256, + ), + ), + ), + ( + "val", + Base( + Bool, + ), + ), + ], + }, + ), + location: Storage { + nonce: Some( + 0, + ), + }, + move_location: Some( + Memory, + ), + } + +note: + ┌─ features/revert.fe:12:5 │ -10 │ ╭ pub def bar() -> u256: -11 │ │ revert +12 │ ╭ pub def bar() -> u256: +13 │ │ revert │ ╰──────────────^ attributes hash: 5931278080780939395 │ = FunctionAttributes { @@ -226,10 +406,10 @@ note: } note: - ┌─ features/revert.fe:13:5 + ┌─ features/revert.fe:15:5 │ -13 │ ╭ pub def revert_custom_error(): -14 │ │ revert Error(msg=1, val=true) +15 │ ╭ pub def revert_custom_error(): +16 │ │ revert Error(msg=1, val=true) │ ╰─────────────────────────────────────^ attributes hash: 8604546375909025578 │ = FunctionAttributes { @@ -242,10 +422,10 @@ note: } note: - ┌─ features/revert.fe:16:5 + ┌─ features/revert.fe:18:5 │ -16 │ ╭ pub def revert_other_error(): -17 │ │ revert OtherError(msg=1, val=true) +18 │ ╭ pub def revert_other_error(): +19 │ │ revert OtherError(msg=1, val=true) │ ╰──────────────────────────────────────────^ attributes hash: 13536596200836451553 │ = FunctionAttributes { @@ -257,17 +437,34 @@ note: ), } +note: + ┌─ features/revert.fe:21:5 + │ +21 │ ╭ pub def revert_other_error_from_sto(): +22 │ │ self.my_other_error = OtherError(msg=1, val=true) +23 │ │ revert self.my_other_error.to_mem() + │ ╰───────────────────────────────────────────^ attributes hash: 10600311300800311694 + │ + = FunctionAttributes { + is_public: true, + name: "revert_other_error_from_sto", + params: [], + return_type: Base( + Unit, + ), + } + note: ┌─ features/revert.fe:9:1 │ 9 │ ╭ contract Foo: -10 │ │ pub def bar() -> u256: -11 │ │ revert -12 │ │ +10 │ │ my_other_error: OtherError +11 │ │ +12 │ │ pub def bar() -> u256: · │ -16 │ │ pub def revert_other_error(): -17 │ │ revert OtherError(msg=1, val=true) - │ ╰──────────────────────────────────────────^ attributes hash: 1654722370251683476 +22 │ │ self.my_other_error = OtherError(msg=1, val=true) +23 │ │ revert self.my_other_error.to_mem() + │ ╰───────────────────────────────────────────^ attributes hash: 15459304479214598396 │ = ContractAttributes { public_functions: [ @@ -297,6 +494,14 @@ note: Unit, ), }, + FunctionAttributes { + is_public: true, + name: "revert_other_error_from_sto", + params: [], + return_type: Base( + Unit, + ), + }, ], init_function: None, events: [], @@ -344,9 +549,9 @@ note: } note: - ┌─ features/revert.fe:14:16 + ┌─ features/revert.fe:16:16 │ -14 │ revert Error(msg=1, val=true) +16 │ revert Error(msg=1, val=true) │ ^^^^^ attributes hash: 563910565138212793 │ = TypeConstructor { @@ -374,9 +579,9 @@ note: } note: - ┌─ features/revert.fe:17:16 + ┌─ features/revert.fe:19:16 │ -17 │ revert OtherError(msg=1, val=true) +19 │ revert OtherError(msg=1, val=true) │ ^^^^^^^^^^ attributes hash: 371870897900126483 │ = TypeConstructor { @@ -403,6 +608,44 @@ note: ), } +note: + ┌─ features/revert.fe:22:31 + │ +22 │ self.my_other_error = OtherError(msg=1, val=true) + │ ^^^^^^^^^^ attributes hash: 371870897900126483 + │ + = TypeConstructor { + typ: Struct( + Struct { + name: "OtherError", + fields: [ + ( + "msg", + Base( + Numeric( + U256, + ), + ), + ), + ( + "val", + Base( + Bool, + ), + ), + ], + }, + ), + } + +note: + ┌─ features/revert.fe:23:16 + │ +23 │ revert self.my_other_error.to_mem() + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ attributes hash: 15856680294290209687 + │ + = ValueAttribute + note: ┌─ features/revert.fe:2:10 │ @@ -448,9 +691,9 @@ note: ) note: - ┌─ features/revert.fe:10:22 + ┌─ features/revert.fe:12:22 │ -10 │ pub def bar() -> u256: +12 │ pub def bar() -> u256: │ ^^^^ attributes hash: 4293763436908729629 │ = Base( @@ -459,4 +702,32 @@ note: ), ) +note: + ┌─ features/revert.fe:10:21 + │ +10 │ my_other_error: OtherError + │ ^^^^^^^^^^ attributes hash: 11110528865914593659 + │ + = Struct( + Struct { + name: "OtherError", + fields: [ + ( + "msg", + Base( + Numeric( + U256, + ), + ), + ), + ( + "val", + Base( + Bool, + ), + ), + ], + }, + ) + diff --git a/crates/analyzer/tests/snapshots/analysis__structs.snap b/crates/analyzer/tests/snapshots/analysis__structs.snap index 2f959cb2b8..7a7f5de4c5 100644 --- a/crates/analyzer/tests/snapshots/analysis__structs.snap +++ b/crates/analyzer/tests/snapshots/analysis__structs.snap @@ -2205,7 +2205,7 @@ note: ┌─ features/structs.fe:37:16 │ 37 │ assert self.my_house.vacant - │ ^^^^^^^^^^^^^^^^^^^^ attributes hash: 12273323640399429874 + │ ^^^^^^^^^^^^^^^^^^^^ attributes hash: 13730979586018155653 │ = ExpressionAttributes { typ: Base( @@ -2216,7 +2216,9 @@ note: 0, ), }, - move_location: None, + move_location: Some( + Value, + ), } note: @@ -2707,7 +2709,7 @@ note: ┌─ features/structs.fe:42:16 │ 42 │ assert self.my_house.vacant - │ ^^^^^^^^^^^^^^^^^^^^ attributes hash: 12273323640399429874 + │ ^^^^^^^^^^^^^^^^^^^^ attributes hash: 13730979586018155653 │ = ExpressionAttributes { typ: Base( @@ -2718,7 +2720,9 @@ note: 0, ), }, - move_location: None, + move_location: Some( + Value, + ), } note: @@ -3209,14 +3213,16 @@ note: ┌─ features/structs.fe:54:16 │ 54 │ assert building.vacant - │ ^^^^^^^^^^^^^^^ attributes hash: 9492448225675818972 + │ ^^^^^^^^^^^^^^^ attributes hash: 793140603415107164 │ = ExpressionAttributes { typ: Base( Bool, ), location: Memory, - move_location: None, + move_location: Some( + Value, + ), } note: diff --git a/crates/analyzer/tests/snapshots/errors__assert_sto_msg_no_copy.snap b/crates/analyzer/tests/snapshots/errors__assert_sto_msg_no_copy.snap new file mode 100644 index 0000000000..0bd7509bb7 --- /dev/null +++ b/crates/analyzer/tests/snapshots/errors__assert_sto_msg_no_copy.snap @@ -0,0 +1,15 @@ +--- +source: crates/analyzer/tests/errors.rs +expression: "error_string(&path, &src)" + +--- +error: value must be copied to memory + ┌─ compile_errors/assert_sto_msg_no_copy.fe:5:23 + │ +5 │ assert false, self.my_string + │ ^^^^^^^^^^^^^^ this value is in storage + │ + = Hint: values located in storage can be copied to memory using the `to_mem` function. + = Example: `self.my_array.to_mem()` + + diff --git a/crates/analyzer/tests/snapshots/errors__for_loop_sto_iter_no_copy.snap b/crates/analyzer/tests/snapshots/errors__for_loop_sto_iter_no_copy.snap new file mode 100644 index 0000000000..b240a158a6 --- /dev/null +++ b/crates/analyzer/tests/snapshots/errors__for_loop_sto_iter_no_copy.snap @@ -0,0 +1,15 @@ +--- +source: crates/analyzer/tests/errors.rs +expression: "error_string(&path, &src)" + +--- +error: value must be copied to memory + ┌─ compile_errors/for_loop_sto_iter_no_copy.fe:5:18 + │ +5 │ for i in self.my_array: + │ ^^^^^^^^^^^^^ this value is in storage + │ + = Hint: values located in storage can be copied to memory using the `to_mem` function. + = Example: `self.my_array.to_mem()` + + diff --git a/crates/analyzer/tests/snapshots/errors__revert_sto_error_no_copy.snap b/crates/analyzer/tests/snapshots/errors__revert_sto_error_no_copy.snap new file mode 100644 index 0000000000..7ebfa2eada --- /dev/null +++ b/crates/analyzer/tests/snapshots/errors__revert_sto_error_no_copy.snap @@ -0,0 +1,15 @@ +--- +source: crates/analyzer/tests/errors.rs +expression: "error_string(&path, &src)" + +--- +error: value must be copied to memory + ┌─ compile_errors/revert_sto_error_no_copy.fe:8:16 + │ +8 │ revert self.my_error + │ ^^^^^^^^^^^^^ this value is in storage + │ + = Hint: values located in storage can be copied to memory using the `to_mem` function. + = Example: `self.my_array.to_mem()` + + diff --git a/crates/test-files/fixtures/compile_errors/assert_sto_msg_no_copy.fe b/crates/test-files/fixtures/compile_errors/assert_sto_msg_no_copy.fe new file mode 100644 index 0000000000..f43dadb548 --- /dev/null +++ b/crates/test-files/fixtures/compile_errors/assert_sto_msg_no_copy.fe @@ -0,0 +1,5 @@ +contract Foo: + my_string: String<42> + + pub def bar(): + assert false, self.my_string \ No newline at end of file diff --git a/crates/test-files/fixtures/compile_errors/for_loop_sto_iter_no_copy.fe b/crates/test-files/fixtures/compile_errors/for_loop_sto_iter_no_copy.fe new file mode 100644 index 0000000000..cf406e7a9e --- /dev/null +++ b/crates/test-files/fixtures/compile_errors/for_loop_sto_iter_no_copy.fe @@ -0,0 +1,6 @@ +contract Foo: + my_array: u256[3] + + pub def bar(): + for i in self.my_array: + pass \ No newline at end of file diff --git a/crates/test-files/fixtures/compile_errors/revert_sto_error_no_copy.fe b/crates/test-files/fixtures/compile_errors/revert_sto_error_no_copy.fe new file mode 100644 index 0000000000..6f18f601cc --- /dev/null +++ b/crates/test-files/fixtures/compile_errors/revert_sto_error_no_copy.fe @@ -0,0 +1,8 @@ +struct MyError: + pass + +contract Foo: + my_error: MyError + + pub def bar(): + revert self.my_error \ No newline at end of file diff --git a/crates/test-files/fixtures/features/assert.fe b/crates/test-files/fixtures/features/assert.fe index ddcf0e8e6b..b9787b9239 100644 --- a/crates/test-files/fixtures/features/assert.fe +++ b/crates/test-files/fixtures/features/assert.fe @@ -1,4 +1,7 @@ contract Foo: + my_bool: bool + my_string: String<5> + pub def bar(baz: u256): assert baz > 5 @@ -6,4 +9,12 @@ contract Foo: assert baz > 5, "Must be greater than five" pub def revert_with(baz: u256, reason: String<1000>): - assert baz > 5, reason \ No newline at end of file + assert baz > 5, reason + + pub def assert_sto_bool(): + self.my_bool = false + assert self.my_bool + + pub def assert_sto_string_msg(): + self.my_string = "hello" + assert false, self.my_string.to_mem() diff --git a/crates/test-files/fixtures/features/for_loop_with_static_array_from_sto.fe b/crates/test-files/fixtures/features/for_loop_with_static_array_from_sto.fe new file mode 100644 index 0000000000..6fd24e392c --- /dev/null +++ b/crates/test-files/fixtures/features/for_loop_with_static_array_from_sto.fe @@ -0,0 +1,10 @@ +contract Foo: + my_array: u256[3] + + pub def bar() -> u256: + self.my_array = [1,2,3] + sum: u256 = 0 + for i in self.my_array.to_mem(): + sum = sum + i + + return sum \ No newline at end of file diff --git a/crates/test-files/fixtures/features/if_statement_test_from_sto.fe b/crates/test-files/fixtures/features/if_statement_test_from_sto.fe new file mode 100644 index 0000000000..eb540531ae --- /dev/null +++ b/crates/test-files/fixtures/features/if_statement_test_from_sto.fe @@ -0,0 +1,20 @@ +contract Foo: + my_bool1: bool + my_bool2: bool + my_bool3: bool + + pub def bar() -> u256: + self.my_bool1 = false + self.my_bool2 = false + self.my_bool3 = true + + if self.my_bool1: + return 26 + + if self.my_bool2: + return 26 + + if self.my_bool3: + return 42 + + return 26 \ No newline at end of file diff --git a/crates/test-files/fixtures/features/return_sum_list_expression_1.fe b/crates/test-files/fixtures/features/return_sum_list_expression_1.fe index ab751810df..18152f6c80 100644 --- a/crates/test-files/fixtures/features/return_sum_list_expression_1.fe +++ b/crates/test-files/fixtures/features/return_sum_list_expression_1.fe @@ -1,7 +1,9 @@ contract Foo: + num_two: u256 pub def bar() -> u256: - my_array: u256[20] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + self.num_two = 2 + my_array: u256[20] = [1, self.num_two, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] sum: u256 = 0 for i in my_array: sum = sum + i diff --git a/crates/test-files/fixtures/features/revert.fe b/crates/test-files/fixtures/features/revert.fe index 1b3aecbc50..cb9f168c88 100644 --- a/crates/test-files/fixtures/features/revert.fe +++ b/crates/test-files/fixtures/features/revert.fe @@ -7,6 +7,8 @@ struct OtherError: val: bool contract Foo: + my_other_error: OtherError + pub def bar() -> u256: revert @@ -15,3 +17,7 @@ contract Foo: pub def revert_other_error(): revert OtherError(msg=1, val=true) + + pub def revert_other_error_from_sto(): + self.my_other_error = OtherError(msg=1, val=true) + revert self.my_other_error.to_mem() diff --git a/crates/test-files/fixtures/features/while_loop_test_from_sto.fe b/crates/test-files/fixtures/features/while_loop_test_from_sto.fe new file mode 100644 index 0000000000..7888fab774 --- /dev/null +++ b/crates/test-files/fixtures/features/while_loop_test_from_sto.fe @@ -0,0 +1,10 @@ +contract Foo: + my_bool: bool + + pub def bar() -> u256: + self.my_bool = false + + while self.my_bool: + pass + + return 42 \ No newline at end of file diff --git a/crates/tests/src/features.rs b/crates/tests/src/features.rs index 625c922053..039f6c7b24 100644 --- a/crates/tests/src/features.rs +++ b/crates/tests/src/features.rs @@ -69,6 +69,14 @@ fn test_revert() { &[uint_token(1), bool_token(true)], ), ); + + validate_revert( + harness.capture_call(&mut executor, "revert_other_error_from_sto", &[]), + &encode_revert( + "OtherError(uint256,bool)", + &[uint_token(1), bool_token(true)], + ), + ); }) } @@ -77,6 +85,16 @@ fn test_assert() { with_executor(&|mut executor| { let harness = deploy_contract(&mut executor, "assert.fe", "Foo", &[]); + validate_revert( + harness.capture_call(&mut executor, "assert_sto_bool", &[]), + &encoded_panic_assert(), + ); + + validate_revert( + harness.capture_call(&mut executor, "assert_sto_string_msg", &[]), + &encode_error_reason("hello"), + ); + validate_revert( harness.capture_call(&mut executor, "bar", &[uint_token(4)]), &encoded_panic_assert(), @@ -107,14 +125,17 @@ fn test_assert() { #[rstest(fixture_file, input, expected, case("for_loop_with_static_array.fe", &[], uint_token(30)), + case("for_loop_with_static_array_from_sto.fe", &[], uint_token(6)), case("for_loop_with_break.fe", &[], uint_token(15)), case("for_loop_with_continue.fe", &[], uint_token(17)), case("while_loop_with_continue.fe", &[], uint_token(1)), case("while_loop.fe", &[], uint_token(3)), + case("while_loop_test_from_sto.fe", &[], uint_token(42)), case("while_loop_with_break.fe", &[], uint_token(1)), case("while_loop_with_break_2.fe", &[], uint_token(1)), case("if_statement.fe", &[uint_token(6)], uint_token(1)), case("if_statement.fe", &[uint_token(4)], uint_token(0)), + case("if_statement_test_from_sto.fe", &[], uint_token(42)), case("if_statement_2.fe", &[uint_token(6)], uint_token(1)), case("if_statement_with_block_declaration.fe", &[], uint_token(1)), case("ternary_expression.fe", &[uint_token(6)], uint_token(1)), diff --git a/newsfragments/493.bugfix.md b/newsfragments/493.bugfix.md new file mode 100644 index 0000000000..9ab63ad8d7 --- /dev/null +++ b/newsfragments/493.bugfix.md @@ -0,0 +1 @@ +Fixed an issue where certain expressions were not being moved to the correct location.