From fdd03abac54f750babe8274e34f9771c40f4d218 Mon Sep 17 00:00:00 2001 From: collin <16715212+collinc97@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:52:10 -0700 Subject: [PATCH 1/3] impl Mapping::contains, Mapping::remove --- compiler/ast/src/functions/core_function.rs | 10 +++++- compiler/parser/src/parser/expression.rs | 6 ++-- compiler/parser/src/parser/statement.rs | 4 +-- .../src/code_generation/visit_expressions.rs | 14 ++++++++ compiler/passes/src/type_checking/checker.rs | 36 +++++++++++++++++++ compiler/span/src/symbol.rs | 2 ++ .../compiler/finalize/contains.out | 12 +++++++ .../compiler/finalize/decrement_fail.out | 2 +- .../compiler/finalize/finalize_fail.out | 2 +- .../compiler/finalize/increment_fail.out | 2 +- .../expectations/compiler/finalize/remove.out | 12 +++++++ tests/tests/compiler/finalize/contains.leo | 18 ++++++++++ tests/tests/compiler/finalize/remove.leo | 25 +++++++++++++ 13 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 tests/expectations/compiler/finalize/contains.out create mode 100644 tests/expectations/compiler/finalize/remove.out create mode 100644 tests/tests/compiler/finalize/contains.leo create mode 100644 tests/tests/compiler/finalize/remove.leo diff --git a/compiler/ast/src/functions/core_function.rs b/compiler/ast/src/functions/core_function.rs index 09a075c56f..86e75277a5 100644 --- a/compiler/ast/src/functions/core_function.rs +++ b/compiler/ast/src/functions/core_function.rs @@ -191,6 +191,8 @@ pub enum CoreFunction { MappingGet, MappingGetOrUse, MappingSet, + MappingRemove, + MappingContains, GroupToXCoordinate, GroupToYCoordinate, @@ -373,6 +375,8 @@ impl CoreFunction { (sym::Mapping, sym::get) => Self::MappingGet, (sym::Mapping, sym::get_or_use) => Self::MappingGetOrUse, (sym::Mapping, sym::set) => Self::MappingSet, + (sym::Mapping, sym::remove) => Self::MappingRemove, + (sym::Mapping, sym::contains) => Self::MappingContains, (sym::group, sym::to_x_coordinate) => Self::GroupToXCoordinate, (sym::group, sym::to_y_coordinate) => Self::GroupToYCoordinate, @@ -556,6 +560,8 @@ impl CoreFunction { Self::MappingGet => 2, Self::MappingGetOrUse => 3, Self::MappingSet => 3, + Self::MappingRemove => 2, + Self::MappingContains => 2, Self::GroupToXCoordinate => 1, Self::GroupToYCoordinate => 1, @@ -582,7 +588,9 @@ impl CoreFunction { | CoreFunction::MappingGet | CoreFunction::MappingGetOrUse | CoreFunction::ChaChaRandScalar - | CoreFunction::MappingSet => true, + | CoreFunction::MappingSet + | CoreFunction::MappingRemove + | CoreFunction::MappingContains => true, CoreFunction::BHP256CommitToAddress | CoreFunction::BHP256CommitToField | CoreFunction::BHP256CommitToGroup diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index fed10cd945..d485a36854 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -329,8 +329,10 @@ impl ParserContext<'_> { match (args.len(), CoreFunction::from_symbols(sym::Mapping, method.name)) { (1, Some(CoreFunction::MappingGet)) | (2, Some(CoreFunction::MappingGetOrUse)) - | (2, Some(CoreFunction::MappingSet)) => { - // Found an instance of `.get`, `.get_or_use`, or `.set` + | (2, Some(CoreFunction::MappingSet)) + | (1, Some(CoreFunction::MappingRemove)) + | (1, Some(CoreFunction::MappingContains)) => { + // Found an instance of `.get`, `.get_or_use`, `.set`, `.remove`, or `.contains`. Ok(Expression::Access(AccessExpression::AssociatedFunction(AssociatedFunction { ty: Type::Identifier(Identifier::new(sym::Mapping)), name: method, diff --git a/compiler/parser/src/parser/statement.rs b/compiler/parser/src/parser/statement.rs index 4b165fc713..4a099eaaba 100644 --- a/compiler/parser/src/parser/statement.rs +++ b/compiler/parser/src/parser/statement.rs @@ -135,14 +135,14 @@ impl ParserContext<'_> { Expression::Identifier(Identifier { name: sym::decrement, .. }) => { self.emit_warning(ParserWarning::deprecated( "decrement", - "Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings.", + "Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings.", place.span(), )); } Expression::Identifier(Identifier { name: sym::increment, .. }) => { self.emit_warning(ParserWarning::deprecated( "increment", - "Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings.", + "Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings.", place.span(), )); } diff --git a/compiler/passes/src/code_generation/visit_expressions.rs b/compiler/passes/src/code_generation/visit_expressions.rs index ea09bebc30..7c07ed48db 100644 --- a/compiler/passes/src/code_generation/visit_expressions.rs +++ b/compiler/passes/src/code_generation/visit_expressions.rs @@ -358,6 +358,20 @@ impl<'a> CodeGenerator<'a> { .expect("failed to write to string"); (String::new(), instruction) } + sym::remove => { + let mut instruction = " remove".to_string(); + // Write the mapping name and the key. + writeln!(instruction, " {}[{}];", arguments[0], arguments[1]).expect("failed to write to string"); + (String::new(), instruction) + } + sym::contains => { + let mut instruction = " contains".to_string(); + let destination_register = get_destination_register(); + // Write the mapping name and the key. + writeln!(instruction, " {}[{}] into {destination_register};", arguments[0], arguments[1]) + .expect("failed to write to string"); + (destination_register, instruction) + } _ => unreachable!("The only variants of Mapping are get, get_or, and set"), }, Type::Identifier(Identifier { name: sym::group, .. }) => { diff --git a/compiler/passes/src/type_checking/checker.rs b/compiler/passes/src/type_checking/checker.rs index 03e71876a5..543fd9b3a5 100644 --- a/compiler/passes/src/type_checking/checker.rs +++ b/compiler/passes/src/type_checking/checker.rs @@ -861,6 +861,42 @@ impl<'a> TypeChecker<'a> { None } } + CoreFunction::MappingRemove => { + // Check that the operation is invoked in a `finalize` block. + if !self.is_finalize { + self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize( + "Mapping::remove", + function_span, + )) + } + // Check that the first argument is a mapping. + if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) { + // Check that the second argument matches the key type of the mapping. + self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1); + // Return the mapping type. + Some(Type::Unit) + } else { + None + } + } + CoreFunction::MappingContains => { + // Check that the operation is invoked in a `finalize` block. + if !self.is_finalize { + self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize( + "Mapping::contains", + function_span, + )) + } + // Check that the first argument is a mapping. + if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) { + // Check that the second argument matches the key type of the mapping. + self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1); + // Return a boolean. + Some(Type::Boolean) + } else { + None + } + } CoreFunction::GroupToXCoordinate | CoreFunction::GroupToYCoordinate => { // Check that the first argument is a group. self.assert_group_type(&arguments[0].0, arguments[0].1); diff --git a/compiler/span/src/symbol.rs b/compiler/span/src/symbol.rs index 9d1f3af6c9..b6a0ac0035 100644 --- a/compiler/span/src/symbol.rs +++ b/compiler/span/src/symbol.rs @@ -153,6 +153,7 @@ symbols! { commit_to_address, commit_to_field, commit_to_group, + contains, get, get_or_use, hash_to_address, @@ -190,6 +191,7 @@ symbols! { rand_u32, rand_u64, rand_u128, + remove, set, to_x_coordinate, to_y_coordinate, diff --git a/tests/expectations/compiler/finalize/contains.out b/tests/expectations/compiler/finalize/contains.out new file mode 100644 index 0000000000..c9ff883dbf --- /dev/null +++ b/tests/expectations/compiler/finalize/contains.out @@ -0,0 +1,12 @@ +--- +namespace: Compile +expectation: Pass +outputs: + - - initial_ast: dd6cce951b7bb1d85a9d077163ed2250323679284134c5f4686bedffff3e2195 + unrolled_ast: dd6cce951b7bb1d85a9d077163ed2250323679284134c5f4686bedffff3e2195 + ssa_ast: 935a4693920001b5de888f5e392e99fa4708c4fede196e03cc017bd01b1f871a + flattened_ast: 07926dfa14daa0d8b378d22bee1d93452ebfa555f7b4bea78a9b20b9c8bbcfdd + inlined_ast: 07926dfa14daa0d8b378d22bee1d93452ebfa555f7b4bea78a9b20b9c8bbcfdd + dce_ast: 07926dfa14daa0d8b378d22bee1d93452ebfa555f7b4bea78a9b20b9c8bbcfdd + bytecode: b8da9b000713a8ba980f8fa632c27a18563320758826f1f6cca908650ed8505a + warnings: "" diff --git a/tests/expectations/compiler/finalize/decrement_fail.out b/tests/expectations/compiler/finalize/decrement_fail.out index 33e3ff820b..02503ade2d 100644 --- a/tests/expectations/compiler/finalize/decrement_fail.out +++ b/tests/expectations/compiler/finalize/decrement_fail.out @@ -2,4 +2,4 @@ namespace: Compile expectation: Fail outputs: - - "Error [ETYC0372005]: Unknown function `decrement`\n --> compiler-test:11:9\n |\n 11 | decrement(amounts, addr, amount);\n | ^^^^^^^^^\nWarning [WPAR0370001]: The keyword `decrement` is deprecated.\n --> compiler-test:11:9\n |\n 11 | decrement(amounts, addr, amount);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings." + - "Error [ETYC0372005]: Unknown function `decrement`\n --> compiler-test:11:9\n |\n 11 | decrement(amounts, addr, amount);\n | ^^^^^^^^^\nWarning [WPAR0370001]: The keyword `decrement` is deprecated.\n --> compiler-test:11:9\n |\n 11 | decrement(amounts, addr, amount);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings." diff --git a/tests/expectations/compiler/finalize/finalize_fail.out b/tests/expectations/compiler/finalize/finalize_fail.out index a60ede96eb..8817d5cca7 100644 --- a/tests/expectations/compiler/finalize/finalize_fail.out +++ b/tests/expectations/compiler/finalize/finalize_fail.out @@ -2,4 +2,4 @@ namespace: Compile expectation: Fail outputs: - - "Error [ETYC0372005]: Unknown function `increment`\n --> compiler-test:12:9\n |\n 12 | increment(account, receiver, amount);\n | ^^^^^^^^^\nError [ETYC0372071]: A finalize block cannot return a value.\n --> compiler-test:19:5\n |\n 19 | finalize public_adder(a: u8, b: u8) -> public u8 {\n 20 | return a + b;\n 21 | }\n | ^\nError [ETYC0372005]: Unknown function `increment`\n --> compiler-test:28:9\n |\n 28 | increment(values, 0u8, 1u8);\n | ^^^^^^^^^\nError [ETYC0372005]: Unknown function `increment`\n --> compiler-test:29:9\n |\n 29 | increment(account, self.caller, 1u64);\n | ^^^^^^^^^\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:12:9\n |\n 12 | increment(account, receiver, amount);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings.\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:28:9\n |\n 28 | increment(values, 0u8, 1u8);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings.\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:29:9\n |\n 29 | increment(account, self.caller, 1u64);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings." + - "Error [ETYC0372005]: Unknown function `increment`\n --> compiler-test:12:9\n |\n 12 | increment(account, receiver, amount);\n | ^^^^^^^^^\nError [ETYC0372071]: A finalize block cannot return a value.\n --> compiler-test:19:5\n |\n 19 | finalize public_adder(a: u8, b: u8) -> public u8 {\n 20 | return a + b;\n 21 | }\n | ^\nError [ETYC0372005]: Unknown function `increment`\n --> compiler-test:28:9\n |\n 28 | increment(values, 0u8, 1u8);\n | ^^^^^^^^^\nError [ETYC0372005]: Unknown function `increment`\n --> compiler-test:29:9\n |\n 29 | increment(account, self.caller, 1u64);\n | ^^^^^^^^^\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:12:9\n |\n 12 | increment(account, receiver, amount);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings.\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:28:9\n |\n 28 | increment(values, 0u8, 1u8);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings.\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:29:9\n |\n 29 | increment(account, self.caller, 1u64);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings." diff --git a/tests/expectations/compiler/finalize/increment_fail.out b/tests/expectations/compiler/finalize/increment_fail.out index f907115afe..c0ec27d139 100644 --- a/tests/expectations/compiler/finalize/increment_fail.out +++ b/tests/expectations/compiler/finalize/increment_fail.out @@ -2,4 +2,4 @@ namespace: Compile expectation: Fail outputs: - - "Error [ETYC0372005]: Unknown function `increment`\n --> compiler-test:11:9\n |\n 11 | increment(amounts, addr, amount);\n | ^^^^^^^^^\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:11:9\n |\n 11 | increment(amounts, addr, amount);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings." + - "Error [ETYC0372005]: Unknown function `increment`\n --> compiler-test:11:9\n |\n 11 | increment(amounts, addr, amount);\n | ^^^^^^^^^\nWarning [WPAR0370001]: The keyword `increment` is deprecated.\n --> compiler-test:11:9\n |\n 11 | increment(amounts, addr, amount);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings." diff --git a/tests/expectations/compiler/finalize/remove.out b/tests/expectations/compiler/finalize/remove.out new file mode 100644 index 0000000000..8f20e2e5a6 --- /dev/null +++ b/tests/expectations/compiler/finalize/remove.out @@ -0,0 +1,12 @@ +--- +namespace: Compile +expectation: Pass +outputs: + - - initial_ast: f17405d1f77533951d0920b702487d1c24008ef33457df29f2e200879a83ffe5 + unrolled_ast: f17405d1f77533951d0920b702487d1c24008ef33457df29f2e200879a83ffe5 + ssa_ast: e5f96ad16a8d9be0e39013503d11a718e6c86449243a3647a4da9a677086fa03 + flattened_ast: cf3f3389913274d14d788dd55d8b0e0df25330bcccb91e58a8f54ed92e7ca1f5 + inlined_ast: cf3f3389913274d14d788dd55d8b0e0df25330bcccb91e58a8f54ed92e7ca1f5 + dce_ast: cf3f3389913274d14d788dd55d8b0e0df25330bcccb91e58a8f54ed92e7ca1f5 + bytecode: 5cae19c2ed8e380e13836028054b78e89448558fcf7af717be63d5737fe92edc + warnings: "" diff --git a/tests/tests/compiler/finalize/contains.leo b/tests/tests/compiler/finalize/contains.leo new file mode 100644 index 0000000000..1bcac8d4c5 --- /dev/null +++ b/tests/tests/compiler/finalize/contains.leo @@ -0,0 +1,18 @@ +/* +namespace: Compile +expectation: Pass +*/ + +program test.aleo { + + mapping balances: address => u32; + + transition foo() -> () { + return then finalize(self.caller); + } + + finalize foo(account: address) -> () { + let expect_false: bool = Mapping::contains(balances, account); + assert(!expect_false); + } +} diff --git a/tests/tests/compiler/finalize/remove.leo b/tests/tests/compiler/finalize/remove.leo new file mode 100644 index 0000000000..c801fbddcf --- /dev/null +++ b/tests/tests/compiler/finalize/remove.leo @@ -0,0 +1,25 @@ +/* +namespace: Compile +expectation: Pass +*/ + +program test.aleo { + + mapping balances: address => u32; + + transition foo() -> () { + return then finalize(self.caller); + } + + finalize foo(account: address) -> () { + Mapping::set(balances, account, 1u32); + + let expect_true: bool = Mapping::contains(balances, account); + assert(expect_true); + + Mapping::remove(balances, account); // Remove the mapping entry for account + + let expect_false: bool = Mapping::contains(balances, account); + assert(!expect_false); + } +} From e995bc0ca93a4e769a491ed6b935822b1b9815d9 Mon Sep 17 00:00:00 2001 From: collin <16715212+collinc97@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:28:56 -0700 Subject: [PATCH 2/3] regen expectations --- tests/expectations/parser/finalize/decrement_fail.out | 2 +- tests/expectations/parser/finalize/increment_fail.out | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/expectations/parser/finalize/decrement_fail.out b/tests/expectations/parser/finalize/decrement_fail.out index 4ab4ad28a1..29066483fb 100644 --- a/tests/expectations/parser/finalize/decrement_fail.out +++ b/tests/expectations/parser/finalize/decrement_fail.out @@ -3,5 +3,5 @@ namespace: ParseStatement expectation: Fail outputs: - "Error [EPAR0370005]: expected ; -- found '['\n --> test:1:10\n |\n 1 | decrement[foo, bar, baz];\n | ^" - - "Error [EPAR0370005]: expected ; -- found ''\n --> test:1:15\n |\n 1 | decrement(floo)\n | ^Warning [WPAR0370001]: The keyword `decrement` is deprecated.\n --> test:1:1\n |\n 1 | decrement(floo)\n | ^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings." + - "Error [EPAR0370005]: expected ; -- found ''\n --> test:1:15\n |\n 1 | decrement(floo)\n | ^Warning [WPAR0370001]: The keyword `decrement` is deprecated.\n --> test:1:1\n |\n 1 | decrement(floo)\n | ^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings." - "Error [EPAR0370005]: expected ; -- found 'foo'\n --> test:1:11\n |\n 1 | decrement foo[bar] by baz;\n | ^^^" diff --git a/tests/expectations/parser/finalize/increment_fail.out b/tests/expectations/parser/finalize/increment_fail.out index d0a4608fa3..31afa5bc60 100644 --- a/tests/expectations/parser/finalize/increment_fail.out +++ b/tests/expectations/parser/finalize/increment_fail.out @@ -3,5 +3,5 @@ namespace: ParseStatement expectation: Fail outputs: - "Error [EPAR0370005]: expected ; -- found '['\n --> test:1:10\n |\n 1 | increment[foo, bar, baz];\n | ^" - - "Error [EPAR0370005]: expected ; -- found ''\n --> test:1:15\n |\n 1 | increment(floo)\n | ^Warning [WPAR0370001]: The keyword `increment` is deprecated.\n --> test:1:1\n |\n 1 | increment(floo)\n | ^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set}` for manipulating on-chain mappings." + - "Error [EPAR0370005]: expected ; -- found ''\n --> test:1:15\n |\n 1 | increment(floo)\n | ^Warning [WPAR0370001]: The keyword `increment` is deprecated.\n --> test:1:1\n |\n 1 | increment(floo)\n | ^^^^^^^^^^^^^^^\n |\n = Use `Mapping::{get, get_or_use, set, remove, contains}` for manipulating on-chain mappings." - "Error [EPAR0370005]: expected ; -- found 'foo'\n --> test:1:11\n |\n 1 | increment foo[bar] by baz;\n | ^^^" From afe41623eeee88cb0158f3bc3b97354df2019b64 Mon Sep 17 00:00:00 2001 From: Collin Chin <16715212+collinc97@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:31:39 -0700 Subject: [PATCH 3/3] Update compiler/passes/src/type_checking/checker.rs Co-authored-by: Alessandro Coglio Signed-off-by: Collin Chin <16715212+collinc97@users.noreply.github.com> --- compiler/passes/src/type_checking/checker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/passes/src/type_checking/checker.rs b/compiler/passes/src/type_checking/checker.rs index 543fd9b3a5..54316183ef 100644 --- a/compiler/passes/src/type_checking/checker.rs +++ b/compiler/passes/src/type_checking/checker.rs @@ -873,7 +873,7 @@ impl<'a> TypeChecker<'a> { if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) { // Check that the second argument matches the key type of the mapping. self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1); - // Return the mapping type. + // Return nothing. Some(Type::Unit) } else { None