From 9346ae483f8c94037f5a173e1c82270da52a24cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Ron=C4=8Devi=C4=87?= Date: Tue, 7 Jan 2025 12:49:23 +0100 Subject: [PATCH 1/3] Implement partial equivalence and extend `forc migrate` tool --- .github/workflows/ci.yml | 10 + Cargo.lock | 12 + Cargo.toml | 1 + forc-plugins/forc-migrate/Cargo.toml | 1 + .../forc-migrate/src/cli/commands/check.rs | 4 +- .../forc-migrate/src/cli/commands/run.rs | 117 ++-- forc-plugins/forc-migrate/src/cli/shared.rs | 8 +- .../forc-migrate/src/matching/lexed_tree.rs | 571 ++++++++++++++--- forc-plugins/forc-migrate/src/matching/mod.rs | 176 +++++- .../forc-migrate/src/matching/typed_tree.rs | 33 +- .../forc-migrate/src/migrations/demo.rs | 156 +++-- .../forc-migrate/src/migrations/mod.rs | 224 +++++-- .../forc-migrate/src/migrations/partial_eq.rs | 595 ++++++++++++++++++ .../forc-migrate/src/migrations/references.rs | 16 +- .../src/migrations/storage_domains.rs | 85 ++- .../forc-migrate/src/modifying/annotated.rs | 30 + .../forc-migrate/src/modifying/attribute.rs | 162 +++++ .../forc-migrate/src/modifying/function.rs | 53 ++ .../forc-migrate/src/modifying/literal.rs | 25 + .../forc-migrate/src/modifying/mod.rs | 53 +- .../forc-migrate/src/modifying/module.rs | 58 ++ .../src/modifying/storage_field.rs | 8 +- .../examples/forc_migrate.md | 4 + sway-ast/src/item/item_trait.rs | 14 + sway-ast/src/path.rs | 8 + sway-core/src/language/lexed/mod.rs | 4 +- .../src/language/parsed/expression/mod.rs | 13 +- .../src/language/ty/declaration/impl_trait.rs | 44 +- sway-core/src/lib.rs | 4 +- .../ast_node/expression/typed_expression.rs | 18 +- .../semantic_analysis/type_check_context.rs | 15 +- sway-features/src/lib.rs | 2 + sway-lib-core/src/codec.sw | 65 ++ sway-lib-core/src/never.sw | 15 + sway-lib-core/src/ops.sw | 311 +++++++++ sway-lib-std/src/address.sw | 9 + sway-lib-std/src/asset_id.sw | 9 + sway-lib-std/src/b512.sw | 9 + sway-lib-std/src/bytes.sw | 16 + sway-lib-std/src/contract_id.sw | 9 + sway-lib-std/src/identity.sw | 13 + sway-lib-std/src/inputs.sw | 14 + sway-lib-std/src/option.sw | 19 + sway-lib-std/src/outputs.sw | 16 + sway-lib-std/src/result.sw | 21 + sway-lib-std/src/string.sw | 9 + sway-lib-std/src/tx.sw | 17 + sway-lib-std/src/u128.sw | 9 + sway-lib-std/src/vm/evm/evm_address.sw | 9 + sway-lsp/src/core/session.rs | 5 +- sway-lsp/src/traverse/lexed_tree.rs | 4 +- swayfmt/src/utils/language/expr/mod.rs | 31 +- swayfmt/src/utils/language/ty.rs | 54 +- .../should_fail/invalid_cfg_arg/stdout.snap | 2 +- .../return_path_analysis/json_abi_oracle.json | 1 - .../return_path_analysis/src/main.sw | 25 + .../return_path_analysis/test.partial_eq.toml | 11 + .../dca/trait_method_neq/src/main.sw | 27 + .../dca/trait_method_neq/test.partial_eq.toml | 5 + .../json_abi_oracle.partial_eq.json} | 32 +- .../language/configurable_consts/src/main.sw | 18 +- .../configurable_consts/test.partial_eq.toml | 23 + .../configurable_tests/json_abi_oracle.json | 284 --------- .../language/configurable_tests/src/main.sw | 21 +- .../configurable_tests/test.partial_eq.toml | 3 + .../enum_in_fn_decl/json_abi_oracle.json | 9 +- .../json_abi_oracle.partial_eq.json | 30 + .../json_abi_oracle_new_encoding.json | 9 +- .../language/enum_in_fn_decl/src/main.sw | 41 ++ .../enum_in_fn_decl/test.partial_eq.toml | 6 + .../generic_impl_self_where/src/main.sw | 194 +++--- .../test.partial_eq.toml.todo | 7 + .../json_abi_oracle.partial_eq.json | 23 + .../language/if_elseif_enum/src/main.sw | 12 + .../if_elseif_enum/test.partial_eq.toml | 6 + .../json_abi_oracle.partial_eq.json | 23 + .../src/asset.sw | 11 +- .../src/utils.sw | 15 +- .../test.partial_eq.toml | 4 + .../json_abi_oracle.partial_eq.json | 23 + .../src/eq_impls.sw | 62 ++ .../test.partial_eq.toml | 5 + .../insert_element_reg_reuse/.gitignore | 2 - .../json_abi_oracle.partial_eq.json | 23 + .../insert_element_reg_reuse/src/main.sw | 58 +- .../test.partial_eq.toml | 5 + .../json_abi_oracle.partial_eq.json | 23 + .../language/local_impl_for_ord/src/main.sw | 9 + .../local_impl_for_ord/test.partial_eq.toml | 6 + .../json_abi_oracle.partial_eq.json | 23 + .../match_expressions_with_self/src/main.sw | 13 + .../test.partial_eq.toml | 6 + .../json_abi_oracle.partial_eq.json | 23 + .../language/mut_ref_empty_type/src/main.sw | 48 +- .../test.partial_eq.toml.todo | 7 + .../src/impls.sw | 137 +++- .../src/main.sw | 147 ++++- .../test.partial_eq.toml.todo | 15 + .../src/impls.sw | 137 +++- .../src/main.sw | 68 +- .../test.partial_eq.toml.todo | 7 + .../dereferencing_operator_index/src/impls.sw | 137 +++- .../test.partial_eq.toml.todo | 7 + .../dereferencing_operator_star/src/impls.sw | 137 +++- .../test.partial_eq.toml.todo | 7 + .../src/impls.sw | 121 +++- .../test.partial_eq.toml.todo | 8 + .../src/main.sw | 79 ++- .../test.partial_eq.toml | 6 + .../src/impls.sw | 141 ++++- .../test.partial_eq.toml.todo | 8 + .../src/impls.sw | 141 ++++- .../test.partial_eq.toml.todo | 8 + .../src/impls.sw | 137 +++- .../src/main.sw | 96 ++- .../test.partial_eq.toml.todo | 8 + .../references_and_generics/src/main.sw | 188 +++++- .../test.partial_eq.toml.todo | 7 + .../referencing_expressions/src/main.sw | 85 ++- .../test.partial_eq.toml.todo | 8 + .../src/main.sw | 165 +++-- .../test.partial_eq.toml.todo | 7 + .../src/impls.sw | 132 +++- .../test.partial_eq.toml.todo | 8 + .../language/supertraits/src/main.sw | 3 +- .../language/trait_nested/src/main.sw | 23 +- .../trait_nested/test.partial_eq.toml | 6 + .../json_abi_oracle.partial_eq.json | 23 + .../language/tuple_trait/src/main.sw | 18 + .../language/tuple_trait/test.partial_eq.toml | 6 + .../language/type_alias/src/main.sw | 20 + .../language/type_alias/test.partial_eq.toml | 6 + .../should_pass/stdlib/chess/src/huge_enum.sw | 75 +++ .../stdlib/chess/test.partial_eq.toml | 6 + .../stdlib/eq_custom_type/src/main.sw | 42 +- .../eq_custom_type/test.partial_eq.toml.todo | 8 + .../json_abi_oracle.partial_eq.json | 23 + .../should_pass/stdlib/eq_generic/src/main.sw | 25 +- .../stdlib/eq_generic/test.partial_eq.toml | 6 + .../option/json_abi_oracle.partial_eq.json | 23 + .../stdlib/option/src/data_structures.sw | 60 ++ .../stdlib/option/test.partial_eq.toml.todo | 8 + .../option_eq/json_abi_oracle.partial_eq.json | 23 + .../should_pass/stdlib/option_eq/src/main.sw | 49 ++ .../stdlib/option_eq/test.partial_eq.toml | 7 + .../result/json_abi_oracle.partial_eq.json | 23 + .../stdlib/result/src/data_structures.sw | 49 ++ .../stdlib/result/test.partial_eq.toml.todo | 7 + .../src/main.sw | 45 +- .../json_abi_oracle.partial_eq.json | 202 ++++++ .../json_storage_slots_oracle.partial_eq.json | 1 + .../test.partial_eq.toml | 5 + .../json_abi_oracle.partial_eq.json | 52 ++ .../json_storage_slots_oracle.partial_eq.json | 1 + .../issue_1512_repro/src/main.sw | 24 +- .../issue_1512_repro/test.partial_eq.toml | 6 + test/src/in_language_tests/Forc.toml | 4 +- .../storage_vec_iter_tests/src/impls.sw | 95 +++ .../src/lib.sw | 18 + .../test_projects/storage_access/src/main.sw | 27 + .../test_projects/storage_init/src/main.sw | 46 ++ .../storage_map_nested/src/main.sw | 22 + .../storage_vec_to_vec/Forc.lock | 13 + .../storage_vec_to_vec/src/main.sw | 9 + 164 files changed, 6464 insertions(+), 1163 deletions(-) create mode 100644 forc-plugins/forc-migrate/src/migrations/partial_eq.rs create mode 100644 forc-plugins/forc-migrate/src/modifying/annotated.rs create mode 100644 forc-plugins/forc-migrate/src/modifying/attribute.rs create mode 100644 forc-plugins/forc-migrate/src/modifying/function.rs create mode 100644 forc-plugins/forc-migrate/src/modifying/literal.rs create mode 100644 forc-plugins/forc-migrate/src/modifying/module.rs delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/test.partial_eq.toml rename test/src/e2e_vm_tests/test_programs/should_pass/language/{configurable_tests/json_abi_oracle_new_encoding.json => configurable_consts/json_abi_oracle.partial_eq.json} (95%) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/test.partial_eq.toml delete mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/test.partial_eq.toml delete mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/.gitignore create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/test.partial_eq.toml.todo create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_storage_slots_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/test.partial_eq.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_storage_slots_oracle.partial_eq.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/test.partial_eq.toml create mode 100644 test/src/sdk-harness/test_projects/storage_vec_to_vec/Forc.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea17447cf90..4d20e27bc1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -475,6 +475,10 @@ jobs: run: cargo run --locked --release -p forc -- build --experimental storage_domains --release --locked --path ./test/src/sdk-harness - name: Cargo Test sway-lib-std - Experimental Feature 'storage_domains' run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture + - name: Build All Tests - Experimental Feature 'partial_eq' + run: cargo run --locked --release -p forc -- build --experimental partial_eq --release --locked --path ./test/src/sdk-harness + - name: Cargo Test sway-lib-std - Experimental Feature 'partial_eq' + run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture forc-run-benchmarks: runs-on: buildjet-4vcpu-ubuntu-2204 @@ -541,14 +545,20 @@ jobs: run: forc build --path sway-lib-core && forc test --path sway-lib-core - name: Run Core Unit Tests - Experimental feature 'storage_domains' run: forc build --experimental storage_domains --path sway-lib-core && forc test --experimental storage_domains --path sway-lib-core + - name: Run Core Unit Tests - Experimental feature 'partial_eq' + run: forc build --experimental partial_eq --path sway-lib-core && forc test --experimental partial_eq --path sway-lib-core - name: Run Std Unit Tests run: forc build --path sway-lib-std && forc test --path sway-lib-std - name: Run Std Unit Tests - Experimental feature 'storage_domains' run: forc build --experimental storage_domains --path sway-lib-std && forc test --experimental storage_domains --path sway-lib-std + - name: Run Std Unit Tests - Experimental feature 'partial_eq' + run: forc build --experimental partial_eq --path sway-lib-std && forc test --experimental partial_eq --path sway-lib-std - name: Run In Language Unit Tests run: forc build --path test/src/in_language_tests && forc test --path test/src/in_language_tests - name: Run In Language Unit Tests - Experimental feature 'storage_domains' run: forc build --experimental storage_domains --path test/src/in_language_tests && forc test --experimental storage_domains --path test/src/in_language_tests + - name: Run In Language Unit Tests - Experimental feature 'partial_eq' + run: forc build --experimental partial_eq --path test/src/in_language_tests && forc test --experimental partial_eq --path test/src/in_language_tests forc-pkg-fuels-deps-check: runs-on: buildjet-4vcpu-ubuntu-2204 diff --git a/Cargo.lock b/Cargo.lock index 6323f544d23..1004776747a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2157,6 +2157,17 @@ dependencies = [ "shared_child", ] +[[package]] +name = "duplicate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97af9b5f014e228b33e77d75ee0e6e87960124f0f4b16337b586a6bec91867b1" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "proc-macro2-diagnostics", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -2867,6 +2878,7 @@ version = "0.66.7" dependencies = [ "anyhow", "clap", + "duplicate", "forc-pkg", "forc-tracing 0.66.7", "forc-util", diff --git a/Cargo.toml b/Cargo.toml index 8140c92ca00..bcff09380b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,6 +133,7 @@ devault = "0.2" dialoguer = "0.11" dirs = "5.0" downcast-rs = "1.2" +duplicate = "2.0" either = "1.9" ethabi = { package = "fuel-ethabi", version = "18.0" } etk-asm = { package = "fuel-etk-asm", version = "0.3.1-dev" } diff --git a/forc-plugins/forc-migrate/Cargo.toml b/forc-plugins/forc-migrate/Cargo.toml index c26988e84c6..a5a6fc8324a 100644 --- a/forc-plugins/forc-migrate/Cargo.toml +++ b/forc-plugins/forc-migrate/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true [dependencies] anyhow.workspace = true clap = { workspace = true, features = ["derive"] } +duplicate.workspace = true forc-pkg.workspace = true forc-tracing.workspace = true forc-util.workspace = true diff --git a/forc-plugins/forc-migrate/src/cli/commands/check.rs b/forc-plugins/forc-migrate/src/cli/commands/check.rs index f99a2b67845..0c643977f9b 100644 --- a/forc-plugins/forc-migrate/src/cli/commands/check.rs +++ b/forc-plugins/forc-migrate/src/cli/commands/check.rs @@ -44,10 +44,10 @@ pub(crate) fn exec(command: Command) -> Result<()> { for migration_step in migration_steps.iter() { let migration_point_spans = match migration_step.kind { MigrationStepKind::Instruction(instruction) => instruction(&program_info)?, - MigrationStepKind::CodeModification(modification, _) => { + MigrationStepKind::CodeModification(modification, ..) => { modification(&mut program_info.as_mut(), DryRun::Yes)? } - MigrationStepKind::Interaction(instruction, _, _) => instruction(&program_info)?, + MigrationStepKind::Interaction(instruction, ..) => instruction(&program_info)?, }; check_result.push((feature, migration_step, migration_point_spans)); diff --git a/forc-plugins/forc-migrate/src/cli/commands/run.rs b/forc-plugins/forc-migrate/src/cli/commands/run.rs index 64329a9ddb9..90129be2725 100644 --- a/forc-plugins/forc-migrate/src/cli/commands/run.rs +++ b/forc-plugins/forc-migrate/src/cli/commands/run.rs @@ -8,7 +8,7 @@ use clap::Parser; use forc_tracing::{println_action_green, println_action_yellow, println_yellow_bold}; use forc_util::{format_diagnostic, fs_locking::is_file_dirty}; use itertools::Itertools; -use sway_ast::{attribute::Annotated, Module}; +use sway_ast::Module; use sway_core::{ language::lexed::{LexedModule, LexedProgram}, Engines, @@ -27,7 +27,10 @@ use crate::{ }, }, get_migration_steps_or_return, instructive_error, - migrations::{DryRun, MigrationStep, MigrationStepKind, MigrationSteps, ProgramInfo}, + migrations::{ + ContinueMigrationProcess, DryRun, InteractionResponse, MigrationStep, MigrationStepKind, + MigrationSteps, ProgramInfo, + }, }; forc_util::cli_examples! { @@ -127,6 +130,7 @@ pub(crate) fn exec(command: Command) -> Result<()> { ) .0; let mut current_feature_migration_has_code_changes = false; + let mut num_of_postponed_steps = 0; for (feature, migration_steps) in migration_steps.iter() { for migration_step in migration_steps.iter() { match migration_step.kind { @@ -145,7 +149,11 @@ pub(crate) fn exec(command: Command) -> Result<()> { println_yellow_bold("If you've already reviewed the above points, you can ignore this info."); } } - MigrationStepKind::CodeModification(modification, manual_migration_actions) => { + MigrationStepKind::CodeModification( + modification, + manual_migration_actions, + continue_migration_process, + ) => { let occurrences_spans = modification(&mut program_info.as_mut(), DryRun::No)?; output_modified_modules( @@ -159,7 +167,9 @@ pub(crate) fn exec(command: Command) -> Result<()> { feature, migration_step, manual_migration_actions, + continue_migration_process, &occurrences_spans, + InteractionResponse::None, &mut current_feature_migration_has_code_changes, ); if stop_migration_process == StopMigrationProcess::Yes { @@ -170,6 +180,7 @@ pub(crate) fn exec(command: Command) -> Result<()> { instruction, interaction, manual_migration_actions, + continue_migration_process, ) => { let instruction_occurrences_spans = instruction(&program_info)?; @@ -183,9 +194,13 @@ pub(crate) fn exec(command: Command) -> Result<()> { // We have occurrences, let's continue with the interaction. if !instruction_occurrences_spans.is_empty() { - let interaction_occurrences_spans = + let (interaction_response, interaction_occurrences_spans) = interaction(&mut program_info.as_mut())?; + if interaction_response == InteractionResponse::PostponeStep { + num_of_postponed_steps += 1; + } + output_modified_modules( &build_instructions.manifest_dir()?, &program_info, @@ -197,7 +212,9 @@ pub(crate) fn exec(command: Command) -> Result<()> { feature, migration_step, manual_migration_actions, + continue_migration_process, &interaction_occurrences_spans, + interaction_response, &mut current_feature_migration_has_code_changes, ); if stop_migration_process == StopMigrationProcess::Yes { @@ -212,7 +229,7 @@ pub(crate) fn exec(command: Command) -> Result<()> { // stop for a review before continuing with the next feature. if current_feature_migration_has_code_changes { if *feature == last_migration_feature { - print_migration_finished_action(); + print_migration_finished_action(num_of_postponed_steps); } else { print_continue_migration_action("Review the changed code"); } @@ -224,7 +241,7 @@ pub(crate) fn exec(command: Command) -> Result<()> { // We've run through all the migration steps. // Print the confirmation message, even if there were maybe infos // displayed for manual reviews. - print_migration_finished_action(); + print_migration_finished_action(num_of_postponed_steps); Ok(()) } @@ -235,16 +252,23 @@ enum StopMigrationProcess { No, } +#[allow(clippy::too_many_arguments)] fn print_modification_result( max_len: usize, feature: &Feature, migration_step: &MigrationStep, manual_migration_actions: &[&str], + continue_migration_process: ContinueMigrationProcess, occurrences_spans: &[Span], + interaction_response: InteractionResponse, current_feature_migration_has_code_changes: &mut bool, ) -> StopMigrationProcess { if occurrences_spans.is_empty() { - print_checked_action(max_len, feature, migration_step); + if interaction_response == InteractionResponse::PostponeStep { + print_postponed_action(max_len, feature, migration_step); + } else { + print_checked_action(max_len, feature, migration_step); + } StopMigrationProcess::No } else { print_changing_code_action(max_len, feature, migration_step); @@ -256,25 +280,35 @@ fn print_modification_result( plural_s(occurrences_spans.len()) ); - // Check if we can proceed with the next migration step or break for manual action. - if !migration_step.has_manual_actions() { - // Mark the feature as having made code changes in the migration, and proceed with the - // next migration step *within the same feature*, if any. - *current_feature_migration_has_code_changes = true; - - StopMigrationProcess::No - } else { - // Display the manual migration actions and stop the further execution of the migration steps. - println!(); - println!("You still need to manually:"); - manual_migration_actions - .iter() - .for_each(|help| println!("- {help}")); - println!(); - println!("{}", detailed_migration_guide_msg(feature)); - print_continue_migration_action("Do the above manual changes"); + // Check if we can proceed with the next migration step, + // or we have a mandatory stop, or a stop for completing manual actions. + match continue_migration_process { + ContinueMigrationProcess::Never => { + print_continue_migration_action("Review the changed code"); - StopMigrationProcess::Yes + StopMigrationProcess::Yes + } + ContinueMigrationProcess::IfNoManualMigrationActionsNeeded => { + if !migration_step.has_manual_actions() { + // Mark the feature as having made code changes in the migration, and proceed with the + // next migration step *within the same feature*, if any. + *current_feature_migration_has_code_changes = true; + + StopMigrationProcess::No + } else { + // Display the manual migration actions and stop the further execution of the migration steps. + println!(); + println!("You still need to manually:"); + manual_migration_actions + .iter() + .for_each(|help| println!("- {help}")); + println!(); + println!("{}", detailed_migration_guide_msg(feature)); + print_continue_migration_action("Do the above manual changes"); + + StopMigrationProcess::Yes + } + } } } } @@ -347,17 +381,10 @@ fn output_changed_lexed_program( modified_modules: &ModifiedModules, lexed_module: &LexedModule, ) -> Result<()> { - if let Some(path) = modified_modules.get_path_if_modified(&lexed_module.tree) { + if let Some(path) = modified_modules.get_path_if_modified(&lexed_module.tree.value) { let mut formatter = Formatter::from_dir(manifest_dir)?; - let annotated_module = Annotated { - // TODO: Handle annotations instead of stripping them. - // See: https://github.com/FuelLabs/sway/issues/6802 - attribute_list: vec![], - value: lexed_module.tree.clone(), - }; - - let code = formatter.format_module(&annotated_module)?; + let code = formatter.format_module(&lexed_module.tree.clone())?; std::fs::write(path, code)?; } @@ -411,8 +438,26 @@ fn print_review_action(max_len: usize, feature: &Feature, migration_step: &Migra ); } -fn print_migration_finished_action() { - println_action_green("Finished", PROJECT_IS_COMPATIBLE); +fn print_postponed_action(max_len: usize, feature: &Feature, migration_step: &MigrationStep) { + println_action_yellow( + "Postponed", + &full_migration_step_title(max_len, feature, migration_step), + ); +} + +fn print_migration_finished_action(num_of_postponed_steps: usize) { + if num_of_postponed_steps > 0 { + println_action_green( + "Finished", + &format!( + "Run `forc migrate` at a later point to resolve {} postponed migration step{}", + number_to_str(num_of_postponed_steps), + plural_s(num_of_postponed_steps), + ), + ) + } else { + println_action_green("Finished", PROJECT_IS_COMPATIBLE); + } } fn print_continue_migration_action(txt: &str) { diff --git a/forc-plugins/forc-migrate/src/cli/shared.rs b/forc-plugins/forc-migrate/src/cli/shared.rs index b3cb0abd83d..7717e14dd54 100644 --- a/forc-plugins/forc-migrate/src/cli/shared.rs +++ b/forc-plugins/forc-migrate/src/cli/shared.rs @@ -248,16 +248,16 @@ pub(crate) fn create_migration_diagnostic( }) .chain(match migration_step.kind { MigrationStepKind::Instruction(_) => vec![], - MigrationStepKind::CodeModification(_, []) => vec![], - MigrationStepKind::CodeModification(_, manual_migration_actions) => { + MigrationStepKind::CodeModification(_, [], _) => vec![], + MigrationStepKind::CodeModification(_, manual_migration_actions, _) => { get_manual_migration_actions_help(manual_migration_actions) } - MigrationStepKind::Interaction(_, _, []) => vec![ + MigrationStepKind::Interaction(_, _, [], _) => vec![ "This migration step will interactively modify the code, based on your input." .to_string(), Diagnostic::help_empty_line(), ], - MigrationStepKind::Interaction(_, _, manual_migration_actions) => vec![ + MigrationStepKind::Interaction(_, _, manual_migration_actions, _) => vec![ "This migration step will interactively modify the code, based on your input." .to_string(), Diagnostic::help_empty_line(), diff --git a/forc-plugins/forc-migrate/src/matching/lexed_tree.rs b/forc-plugins/forc-migrate/src/matching/lexed_tree.rs index 25dce32ba6e..f6bebce6d63 100644 --- a/forc-plugins/forc-migrate/src/matching/lexed_tree.rs +++ b/forc-plugins/forc-migrate/src/matching/lexed_tree.rs @@ -1,150 +1,475 @@ -//! This module contains helper functions for matching elements within a lexed program. +//! This module contains helper functions for matching elements within a mutable or immutable lexed tree. +//! Functions are grouped in two submodules, [self::matchers] and [self::matchers_mut]. Both modules +//! contain the same functions, that differ only in the mutability of their arguments and returned types. -use super::{any_mut, LexedElementsMatcher, LexedElementsMatcherDeep}; -use sway_ast::{ItemKind, ItemStorage, StorageEntry, StorageField}; -use sway_core::language::lexed::{LexedModule, LexedProgram}; +use super::*; +use duplicate::duplicate_item; +use sway_ast::{ + attribute::Annotated, ItemImpl, ItemKind, ItemStorage, Module, StorageEntry, StorageField, +}; +use sway_ast::{Literal, PathType}; +use sway_core::language::{ + lexed::{LexedModule, LexedProgram}, + ty::TyImplSelfOrTrait, +}; +use sway_types::Spanned; -impl LexedElementsMatcher for LexedProgram { - fn match_elems<'a, F>(&'a mut self, predicate: F) -> impl Iterator +// To avoid extensive code duplication, the `duplicate_item` macro is used. +// When adding new matchers, the proposed and simplest approach is the following: +// - implement either a mutable or immutable version, as needed in the concrete new migration step. +// - keep the new matcher function or trait implementation at first out of the `matchers/_mut` modules +// and use it in the concrete migration step directly. +// - once properly tested, move the function or trait implementation inside of the `__mod_name` +// and perform the replacements of used identifiers. E.g., replace every occurrence of `iter` or +// `iter_mut` with `__iter`. + +// We need to specify `self` explicitly so that `duplicate_item` can produce +// both variants: `self: &'a Self` and `self: &'a mut Self`. +#[allow(clippy::needless_arbitrary_self_type)] +// We need to specify `'a` explicitly to be able to specify template implementation +// that will work both for immutable and mutable case. +#[allow(clippy::needless_lifetimes)] +#[duplicate_item( + // Module name, `matchers` or `matchers_mut`. + __mod_name + // Traits to implement. + __ElementsMatcher __ElementsMatcherDeep __LocateAnnotated __LocateAsAnnotated + // Trait methods. + __match_elems __match_elems_deep __locate_annotated __locate_as_annotated + // Common implementation elements, e.g. functions like `iter().` + __ref_type(type) __ref_mut(value) __ref(value) __iter __as_ref_mut __any; + + [matchers] + [LexedElementsMatcher] [LexedElementsMatcherDeep] [LexedLocateAnnotated] [LexedLocateAsAnnotated] + [match_elems] [match_elems_deep] [locate_annotated] [locate_as_annotated] + [&'a type] [value] [&value] [iter] [as_ref] [any]; + + [matchers_mut] + [LexedElementsMatcherMut] [LexedElementsMatcherDeepMut] [LexedLocateAnnotatedMut] [LexedLocateAsAnnotatedMut] + [match_elems_mut] [match_elems_deep_mut] [locate_annotated_mut] [locate_as_annotated_mut] + [&'a mut type] [ref mut value] [&mut value] [iter_mut] [as_mut] [any_mut]; +)] +#[allow(dead_code)] +pub mod __mod_name { + use super::*; + + impl __ElementsMatcher for Module { + fn __match_elems<'a, F>( + self: __ref_type([Self]), + predicate: F, + ) -> impl Iterator + where + F: Fn(&__ref_type([ItemFn])) -> bool + Clone + 'a, + ItemFn: 'a, + { + self.items + .__iter() + .map(|annotated| __ref([annotated.value])) + .filter_map(|decl| match decl { + sway_ast::ItemKind::Fn(module_fn) => Some(module_fn), + _ => None, + }) + .filter(predicate) + } + } + + impl __ElementsMatcher for LexedProgram { + fn __match_elems<'a, F>( + self: __ref_type([Self]), + predicate: F, + ) -> impl Iterator + where + F: Fn(&__ref_type([ItemStorage])) -> bool + Clone + 'a, + ItemStorage: 'a, + { + // Storage can be declared only in the root module of a contract. + self.root.__match_elems(predicate) + } + } + + impl __ElementsMatcher for LexedModule { + fn __match_elems<'a, F>( + self: __ref_type([Self]), + predicate: F, + ) -> impl Iterator + where + F: Fn(&__ref_type([ItemStorage])) -> bool + Clone + 'a, + ItemStorage: 'a, + { + self.tree + .value + .items + .__iter() + .map(|annotated_item| __ref([annotated_item.value])) + .filter_map(move |decl| match decl { + ItemKind::Storage(__ref_mut([item_storage])) => { + if predicate(&item_storage) { + Some(item_storage) + } else { + None + } + } + _ => None, + }) + } + } + + impl __ElementsMatcher for ItemStorage { + fn __match_elems<'a, F>( + self: __ref_type([Self]), + predicate: F, + ) -> impl Iterator + where + F: Fn(&__ref_type([StorageField])) -> bool + Clone + 'a, + StorageField: 'a, + { + self.entries + .inner + .__iter() + .map(|annotated_item| __ref([annotated_item.value])) + .filter_map(move |storage_entry| { + storage_entry + .field + .__as_ref_mut() + .filter(|sf| predicate(sf)) + }) + } + } + + impl __ElementsMatcherDeep for ItemStorage { + fn __match_elems_deep<'a, F>( + self: __ref_type([Self]), + predicate: F, + ) -> Vec<__ref_type([StorageField])> + where + F: Fn(&__ref_type([StorageField])) -> bool + Clone + 'a, + StorageField: 'a, + { + fn recursively_collect_storage_fields_in_storage_entry<'a, P>( + result: &mut Vec<__ref_type([StorageField])>, + predicate: P, + storage_entry: __ref_type([StorageEntry]), + ) where + P: Fn(&__ref_type([StorageField])) -> bool + Clone + 'a, + { + if let Some(sf) = __ref([storage_entry.field]) { + if predicate(&sf) { + result.push(sf) + } + } + + if let Some(namespace) = __ref([storage_entry.namespace]) { + namespace + .inner + .__iter() + .map(|annotated_item| __ref([annotated_item.value])) + .for_each(|storage_entry| { + recursively_collect_storage_fields_in_storage_entry( + result, + predicate.clone(), + storage_entry.__as_ref_mut(), + ) + }); + } + } + + let mut result = vec![]; + self.entries + .inner + .__iter() + .map(|annotated_item| __ref([annotated_item.value])) + .for_each(|storage_entry| { + recursively_collect_storage_fields_in_storage_entry( + &mut result, + predicate.clone(), + storage_entry, + ) + }); + + result + } + } + + impl __ElementsMatcher> for Module { + fn __match_elems<'a, F>( + self: __ref_type([Self]), + predicate: F, + ) -> impl Iterator])> + where + F: Fn(&__ref_type([Annotated])) -> bool + Clone + 'a, + Annotated: 'a, + { + self.items.__iter().filter(predicate) + } + } + + impl __ElementsMatcher for ItemImpl { + fn __match_elems<'a, F>( + self: __ref_type([Self]), + predicate: F, + ) -> impl Iterator + where + F: Fn(&__ref_type([PathType])) -> bool + Clone + 'a, + PathType: 'a, + { + self.where_clause_opt + .__iter() + .flat_map(|where_clause| where_clause.bounds.__iter()) + .flat_map(move |bound| bound.bounds.__iter().filter(predicate.clone())) + } + } + + impl __LocateAnnotated for Module { + fn __locate_annotated<'a>( + self: __ref_type([Self]), + ty_element: &TyImplSelfOrTrait, + ) -> Option<(__ref_type([Vec]), __ref_type([ItemImpl]))> { + self.items + .__iter() + .filter_map(|annotated| match __ref([annotated.value]) { + ItemKind::Impl(item_impl) => { + Some((__ref([annotated.attribute_list]), item_impl)) + } + // ItemKind::Impl(__ref_mut([item_impl])) => Some((__ref([annotated.attribute_list]), item_impl)), + _ => None, + }) + .find(|(_attributes, item_impl)| item_impl.span() == ty_element.span) + } + } + + impl __LocateAsAnnotated for Module { + fn __locate_as_annotated<'a>( + self: __ref_type([Self]), + ty_element: &TyImplSelfOrTrait, + ) -> Option<__ref_type([Annotated])> { + self.items + .__iter() + .find(|annotated| match &annotated.value { + ItemKind::Impl(item_impl) => item_impl.span() == ty_element.span, + _ => false, + }) + } + } + + use sway_ast::{ + attribute::{Attribute, AttributeArg}, + AttributeDecl, CommaToken, Parens, Punctuated, + }; + use sway_types::constants::CFG_ATTRIBUTE_NAME; + + pub(crate) fn storage_decl<'a, P>(parent: __ref_type([P])) -> Option<__ref_type([ItemStorage])> where - F: Fn(&&'a mut ItemStorage) -> bool + Clone + 'a, - ItemStorage: 'a, + P: __ElementsMatcher, { - // Storage can be declared only in the root of a contract. - self.root.match_elems(predicate) + parent.__match_elems(__any).next() } -} -impl LexedElementsMatcher for LexedModule { - fn match_elems<'a, F>(&'a mut self, predicate: F) -> impl Iterator + pub(crate) fn storage_fields<'a, P, F>( + parent: __ref_type([P]), + predicate: F, + ) -> impl Iterator where - F: Fn(&&'a mut ItemStorage) -> bool + Clone + 'a, - ItemStorage: 'a, + F: Fn(&__ref_type([StorageField])) -> bool + Clone + 'a, + P: __ElementsMatcher, { - self.tree - .items - .iter_mut() - .map(|annotated_item| &mut annotated_item.value) - .filter_map(move |decl| match decl { - ItemKind::Storage(ref mut item_storage) => { - if predicate(&item_storage) { - Some(item_storage) - } else { - None - } - } - _ => None, - }) + parent.__match_elems(predicate) + } + + pub(crate) fn storage_fields_deep<'a, S, F>( + scope: __ref_type([S]), + predicate: F, + ) -> Vec<__ref_type([StorageField])> + where + F: Fn(&__ref_type([StorageField])) -> bool + Clone + 'a, + S: __ElementsMatcherDeep, + { + scope.__match_elems_deep(predicate) } -} -impl LexedElementsMatcher for ItemStorage { - fn match_elems<'a, F>(&'a mut self, predicate: F) -> impl Iterator + pub(crate) fn attributes<'a, F>( + attributes: __ref_type([[AttributeDecl]]), + predicate: F, + ) -> impl Iterator where - F: Fn(&&'a mut StorageField) -> bool + Clone + 'a, - StorageField: 'a, + F: Fn(&__ref_type([Attribute])) -> bool + Clone + 'a, { - self.entries - .inner - .iter_mut() - .map(|annotated_item| &mut annotated_item.value) - .filter_map(move |storage_entry| { - storage_entry.field.as_mut().filter(|sf| predicate(sf)) + attributes + .__iter() + .flat_map(|attr| attr.attribute.inner.__iter()) + .filter(predicate) + } + + /// Returns all `cfg` attributes found in `attributes`. + pub(crate) fn cfg_attributes<'a>( + attributes: __ref_type([[AttributeDecl]]), + ) -> impl Iterator { + attributes + .__iter() + .flat_map(|attr| attr.attribute.inner.__iter()) + .filter(|attr| attr.name.as_str() == CFG_ATTRIBUTE_NAME) + } + + /// Returns all `cfg` attributes that act as only attribute within + /// an [AttributeDecl] and have exactly one argument. + /// + /// E.g.: + /// - `#[cfg(experimental_feature = true)]` will be returned, + /// - `#[cfg(experimental_feature = true, experimental_other_feature = false)]` will not, + /// - `#[test, cfg(experimental_feature = true)]` will also not be returned. + pub(crate) fn cfg_attributes_standalone_single_arg<'a>( + attributes: __ref_type([[AttributeDecl]]), + ) -> impl Iterator { + attributes + .__iter() + .filter(|attr| attr.attribute.inner.iter().count() == 1) + .flat_map(|attr| attr.attribute.inner.__iter()) + .filter(|attr| attr.name.as_str() == CFG_ATTRIBUTE_NAME) + .filter(|attr| { + attr.args + .as_ref() + .is_some_and(|args| args.inner.iter().count() == 1) }) } -} -impl LexedElementsMatcherDeep for ItemStorage { - fn match_elems_deep<'a, F>(&'a mut self, predicate: F) -> Vec<&'a mut StorageField> + /// Returns the first [AttributeArg] of the first occurrence of a `cfg` attribute within `attributes`, + /// that satisfies the `predicate`. + pub(crate) fn cfg_attribute_arg<'a, F>( + attributes: __ref_type([[AttributeDecl]]), + predicate: F, + ) -> Option<__ref_type([AttributeArg])> where - F: Fn(&&'a mut StorageField) -> bool + Clone + 'a, - StorageField: 'a, + F: Fn(&__ref_type([AttributeArg])) -> bool + Clone + 'a, { - fn recursively_collect_storage_fields_in_storage_entry<'a, P>( - result: &mut Vec<&'a mut StorageField>, - predicate: P, - storage_entry: &'a mut StorageEntry, - ) where - P: Fn(&&'a mut StorageField) -> bool + Clone + 'a, - { - if let Some(ref mut sf) = storage_entry.field { - if predicate(&sf) { - result.push(sf) - } + for cfg_attribute in cfg_attributes(attributes) { + match cfg_attribute.args.__as_ref_mut() { + Some(args) => match attribute_arg(args, predicate.clone()) { + Some(arg) => return Some(arg), + None => continue, + }, + None => continue, } + } - if let Some(ref mut namespace) = storage_entry.namespace { - namespace - .inner - .iter_mut() - .map(|annotated_item| &mut annotated_item.value) - .for_each(|storage_entry| { - recursively_collect_storage_fields_in_storage_entry( - result, - predicate.clone(), - storage_entry.as_mut(), - ) - }); + None + } + + /// Returns the first `cfg` [Attribute] that act as only attribute within + /// an [AttributeDecl] and have exactly one argument that satisfies the `predicate`. + pub(crate) fn cfg_attribute_standalone_single_arg<'a, N, F>( + attributes: __ref_type([[AttributeDecl]]), + arg_name: &N, + arg_val_predicate: F, + ) -> Option<__ref_type([Attribute])> + where + N: AsRef + ?Sized, + F: Fn(&&Option) -> bool + Clone, + { + for cfg_attribute in cfg_attributes_standalone_single_arg(attributes) { + // We for sure have a `cfg` attribute with exactly one argument. + // Thus, the unwraps are safe. + let arg = cfg_attribute + .args + .as_ref() + .unwrap() + .inner + .iter() + .next() + .unwrap(); + if arg.name.as_str() == arg_name.as_ref() && arg_val_predicate(&&arg.value) { + return Some(cfg_attribute); } } - let mut result = vec![]; - self.entries - .inner - .iter_mut() - .map(|annotated_item| &mut annotated_item.value) - .for_each(|storage_entry| { - recursively_collect_storage_fields_in_storage_entry( - &mut result, - predicate.clone(), - storage_entry, - ) - }); + None + } - result + /// Returns the first attribute in `attributes` that satisfies the `predicate`. + pub(crate) fn attribute<'a, F>( + attributes: __ref_type([[AttributeDecl]]), + predicate: F, + ) -> Option<__ref_type([Attribute])> + where + F: Fn(&__ref_type([Attribute])) -> bool + Clone + 'a, + { + attributes + .__iter() + .flat_map(|attr| attr.attribute.inner.__iter()) + .find(predicate) } -} -pub mod matchers { - use super::*; + pub(crate) fn attribute_args<'a, F>( + attribute_args: __ref_type([Parens>]), + predicate: F, + ) -> impl Iterator + where + F: Fn(&__ref_type([AttributeArg])) -> bool + Clone + 'a, + { + attribute_args.inner.__iter().filter(predicate) + } - pub(crate) fn storage_decl

(parent: &mut P) -> Option<&mut ItemStorage> + pub(crate) fn attribute_arg<'a, F>( + attribute_args: __ref_type([Parens>]), + predicate: F, + ) -> Option<__ref_type([AttributeArg])> where - P: LexedElementsMatcher, + F: Fn(&__ref_type([AttributeArg])) -> bool + Clone + 'a, { - parent.match_elems(any_mut).next() + attribute_args.inner.__iter().find(predicate) } - #[allow(dead_code)] - pub(crate) fn storage_fields<'a, P, F>( - parent: &'a mut P, + pub(crate) fn impl_self_or_trait_decls_annotated<'a, P>( + parent: __ref_type([P]), + ) -> impl Iterator])> + where + P: __ElementsMatcher>, + { + parent.__match_elems(|annotated| matches!(annotated.value, ItemKind::Impl(_))) + } + + /// Returns all trait constraints for all constrained generic + /// arguments in the `parent`, that satisfy the `predicate`. + /// The result is flattened and cumulative. This means that + /// all the trait constraints will be collected from all + /// the constraint arguments, even if there are duplicates. + /// + /// E.g., for this `where` clause and no predicate: + /// ```ignore + /// where A: Eq + AbiEncode + SomeTrait, + /// B: Eq + SomeTrait, + /// ``` + /// The returned trait constraints will be: + /// ```ignore + /// Eq, AbiEncode, SomeTrait, Eq, SomeTrait + /// ``` + pub(crate) fn trait_constraints<'a, P, F>( + parent: __ref_type([P]), predicate: F, - ) -> impl Iterator + ) -> impl Iterator where - F: Fn(&&'a mut StorageField) -> bool + Clone + 'a, - P: LexedElementsMatcher, + F: Fn(&__ref_type([PathType])) -> bool + Clone + 'a, + P: __ElementsMatcher, { - parent.match_elems(predicate) + parent.__match_elems(predicate) } - pub(crate) fn storage_fields_deep<'a, S, F>( - scope: &'a mut S, + pub(crate) fn functions<'a, P, F>( + parent: __ref_type([P]), predicate: F, - ) -> Vec<&'a mut StorageField> + ) -> impl Iterator where - F: Fn(&&'a mut StorageField) -> bool + Clone + 'a, - S: LexedElementsMatcherDeep, + F: Fn(&__ref_type([ItemFn])) -> bool + Clone + 'a, + P: __ElementsMatcher, { - scope.match_elems_deep(predicate) + parent.__match_elems(predicate) } } +#[allow(dead_code)] pub mod predicates { pub mod lexed_storage_field { use super::super::*; - #[allow(dead_code)] - pub(crate) fn with_in_keyword(storage_field: &&mut StorageField) -> bool { + pub(crate) fn with_in_keyword(storage_field: &&StorageField) -> bool { storage_field.key_expr.is_some() } @@ -152,4 +477,46 @@ pub mod predicates { storage_field.key_expr.is_none() } } + + pub mod item_impl { + use super::super::*; + + pub(crate) fn implements_trait<'a, N: AsRef + ?Sized>( + trait_name: &'a N, + ) -> impl Fn(&&'a ItemImpl) -> bool { + move |item_impl: &&ItemImpl| { + if let Some((path, _for_token)) = &item_impl.trait_opt { + path.last_segment().name.as_str() == trait_name.as_ref() + } else { + false + } + } + } + } + + pub mod literal { + use sway_ast::literal::{LitBool, LitBoolType}; + + use super::super::*; + + pub(crate) fn is_bool_true(literal: &Literal) -> bool { + matches!( + literal, + Literal::Bool(LitBool { + kind: LitBoolType::True, + .. + }) + ) + } + + pub(crate) fn is_bool_false(literal: &Literal) -> bool { + matches!( + literal, + Literal::Bool(LitBool { + kind: LitBoolType::False, + .. + }) + ) + } + } } diff --git a/forc-plugins/forc-migrate/src/matching/mod.rs b/forc-plugins/forc-migrate/src/matching/mod.rs index 989d1398544..70b1baba246 100644 --- a/forc-plugins/forc-migrate/src/matching/mod.rs +++ b/forc-plugins/forc-migrate/src/matching/mod.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] //! This module contains common API for matching elements //! within a lexed or a typed tree. //! @@ -21,7 +22,23 @@ //! order to move cumbersome and error-prone matching code out of the migration //! logic. //! -//! Migrations will use module level match functions to either search directly +//! Note that, although similar to static analysis tools like, e.g. Rust's +//! [Clippy](https://doc.rust-lang.org/clippy/), `forc migrate` is significantly +//! different. Instead of providing hundreds of independent lints that +//! automatically check for localized issues, migrations provide only a handful +//! of migration steps, that are orchestrated withing a single migration process, +//! some of them possibly being interactive. +//! +//! Each migration step, in general, wants to take a look at a larger scope at a time, +//! often a module. This makes a typical approach, of using fine-grain visitor functions +//! less applicable. Also, the goal is to empower non-compiler developers to write +//! migrations. +//! +//! All this led to the design in which a single migration step is in focus, and can: +//! - search for elements of interest using the match functions, +//! - build new and modify existing lexed elements using the [super::modifying], +//! +//! Migrations will use match functions to either search directly //! within a parent or recursively (deep) within a scope. Match functions can //! accept predicates to filter the searched elements. The predicates deliberately //! accept `&&TElement` or `&&mut TElement` so that can be easily passed to @@ -29,11 +46,9 @@ //! //! ## Matching elements in trees //! -//! Functions matching on lexed tree require mutable references as -//! input and return mutable references as output. This is according -//! to the premise that the non-code-modifying analysis will be done -//! on typed trees, while the code-modifying will be done on the -//! mutable lexed tree, as well as the typed tree. +//! Functions matching on lexed trees are coming in two variants, immutable and mutable. +//! They differ in the mutability of their arguments and returned types, but +//! otherwise implement the same matching logic. //! //! Matching can be done either directly within a parent, or recursively //! within a scope. E.g., we can match for `StorageField`s that are @@ -56,7 +71,7 @@ //! order to change it, but will need additional information from its typed tree //! counterpart, `TyStorageField`, or vice versa. The [TyLocate] trait offers //! the [TyLocate::locate] method for finding a typed equivalent of a lexed -//! element. The [LexedLocate] does the opposite. +//! element. The [LexedLocate] and [LexedLocateMut] do the opposite. //! //! Locating an equivalent will in most of the cases be implemented via equality //! of spans. Locating can also cause multiple traversals of the same part of @@ -65,11 +80,14 @@ mod lexed_tree; mod typed_tree; +use sway_ast::attribute::{Annotated, Attribute, AttributeArg}; +use sway_ast::{AttributeDecl, ItemFn, PathType}; pub(crate) use typed_tree::matchers as ty_match; -pub(crate) use typed_tree::predicates::ty_storage_field; +pub(crate) use typed_tree::predicates::*; pub(crate) use lexed_tree::matchers as lexed_match; -pub(crate) use lexed_tree::predicates::lexed_storage_field; +pub(crate) use lexed_tree::matchers_mut as lexed_match_mut; +pub(crate) use lexed_tree::predicates::*; /// Matches for typed tree elements of type `T` located **directly** within /// the typed tree element `self`. @@ -104,40 +122,110 @@ pub(crate) trait TyLocate { /// the lexed tree element `self`. /// /// The matched elements must satisfy the `predicate`. -pub(crate) trait LexedElementsMatcher { - fn match_elems<'a, F>(&'a mut self, predicate: F) -> impl Iterator +pub(crate) trait LexedElementsMatcherMut { + fn match_elems_mut<'a, F>(&'a mut self, predicate: F) -> impl Iterator where F: Fn(&&'a mut T) -> bool + Clone + 'a, T: 'a; } +pub(crate) trait LexedElementsMatcher { + fn match_elems<'a, F>(&'a self, predicate: F) -> impl Iterator + where + F: Fn(&&'a T) -> bool + Clone + 'a, + T: 'a; +} + /// Matches for lexed tree elements of type `T` located **recursively** within /// the lexed tree element `self` or any of its children. The meaning of a /// "child" depends on the exact tree element `self`. /// /// The matched elements must satisfy the `predicate`. -pub(crate) trait LexedElementsMatcherDeep { - fn match_elems_deep<'a, F>(&'a mut self, predicate: F) -> Vec<&'a mut T> +pub(crate) trait LexedElementsMatcherDeepMut { + fn match_elems_deep_mut<'a, F>(&'a mut self, predicate: F) -> Vec<&'a mut T> where F: Fn(&&'a mut T) -> bool + Clone + 'a, T: 'a; } +pub(crate) trait LexedElementsMatcherDeep { + fn match_elems_deep<'a, F>(&'a self, predicate: F) -> Vec<&'a T> + where + F: Fn(&&'a T) -> bool + Clone + 'a, + T: 'a; +} + +/// Within a lexed tree element `self`, locates and returns the element of type `Lexed`, +/// that is the lexed equivalent of the `ty_element`. +pub(crate) trait LexedLocateMut { + fn locate_mut(&mut self, ty_element: &Ty) -> Option<&mut Lexed>; +} + /// Within a lexed tree element `self`, locates and returns the element of type `Lexed`, /// that is the lexed equivalent of the `ty_element`. -#[allow(dead_code)] pub(crate) trait LexedLocate { - fn locate(&mut self, ty_element: &Ty) -> Option<&mut Lexed>; + fn locate(&self, ty_element: &Ty) -> Option<&Lexed>; +} + +/// Within a lexed tree element `self`, locates and returns the element of type `Lexed`, +/// that is the lexed equivalent of the `ty_element`, together with its annotations. +pub(crate) trait LexedLocateAnnotatedMut { + fn locate_annotated_mut<'a>( + &'a mut self, + ty_element: &Ty, + ) -> Option<(&'a mut Vec, &'a mut Lexed)>; +} + +/// Within a lexed tree element `self`, locates and returns the element of type `Lexed`, +/// that is the lexed equivalent of the `ty_element`, together with its annotations. +pub(crate) trait LexedLocateAnnotated { + fn locate_annotated<'a>( + &'a self, + ty_element: &Ty, + ) -> Option<(&'a Vec, &'a Lexed)>; +} + +/// Within an annotated lexed tree element `self`, locates and returns the element of type `LexedAnnotated`, +/// that is the annotated lexed equivalent of the `ty_element`. +pub(crate) trait LexedLocateAsAnnotatedMut { + fn locate_as_annotated_mut( + &mut self, + ty_element: &Ty, + ) -> Option<&mut Annotated>; +} + +/// Within an annotated lexed tree element `self`, locates and returns the element of type `LexedAnnotated`, +/// that is the annotated lexed equivalent of the `ty_element`. +pub(crate) trait LexedLocateAsAnnotated { + fn locate_as_annotated(&self, ty_element: &Ty) -> Option<&Annotated>; } -/// A predicate that returns true for any input. -/// Convenient to use in [TyElementsMatcher] and [TyElementsMatcherDeep]. +impl LexedLocateMut for T +where + T: LexedLocateAnnotatedMut, +{ + fn locate_mut(&mut self, ty_element: &Ty) -> Option<&mut Lexed> { + self.locate_annotated_mut(ty_element) + .map(|annotated| annotated.1) + } +} + +impl LexedLocate for T +where + T: LexedLocateAnnotated, +{ + fn locate(&self, ty_element: &Ty) -> Option<&Lexed> { + self.locate_annotated(ty_element) + .map(|annotated| annotated.1) + } +} + +/// A predicate that returns true for any immutable input. pub(crate) fn any(_t: &&T) -> bool { true } -/// A predicate that returns true for any input. -/// Convenient to use in [LexedElementsMatcher] and [LexedElementsMatcherDeep]. +/// A predicate that returns true for any mutable input. pub(crate) fn any_mut(_t: &&mut T) -> bool { true } @@ -249,3 +337,53 @@ where res } } + +/// Trait for inspecting if a tree element has the expected name. +pub(crate) trait WithName { + /// Returns true if `Self` has the name `name`. + fn with_name + ?Sized>(&self, name: &N) -> bool; +} + +/// Returns a predicate that evaluates to true if a [WithName] +/// implementor has the name equal to `name`. +pub(crate) fn with_name(name: &N) -> impl Fn(&&T) -> bool + Clone + '_ +where + T: WithName, + N: AsRef + ?Sized, +{ + move |t: &&T| t.with_name(name) +} + +/// Returns a predicate that evaluates to true if a [WithName] +/// implementor has the name equal to `name`. +pub(crate) fn with_name_mut(name: &N) -> impl Fn(&&mut T) -> bool + Clone + '_ +where + T: WithName, + N: AsRef + ?Sized, +{ + move |t: &&mut T| t.with_name(name) +} + +impl WithName for Attribute { + fn with_name + ?Sized>(&self, name: &N) -> bool { + self.name.as_str() == name.as_ref() + } +} + +impl WithName for AttributeArg { + fn with_name + ?Sized>(&self, name: &N) -> bool { + self.name.as_str() == name.as_ref() + } +} + +impl WithName for PathType { + fn with_name + ?Sized>(&self, name: &N) -> bool { + self.last_segment().name.as_str() == name.as_ref() + } +} + +impl WithName for ItemFn { + fn with_name + ?Sized>(&self, name: &N) -> bool { + self.fn_signature.name.as_str() == name.as_ref() + } +} diff --git a/forc-plugins/forc-migrate/src/matching/typed_tree.rs b/forc-plugins/forc-migrate/src/matching/typed_tree.rs index a15387c6c6a..d5c2803e3f5 100644 --- a/forc-plugins/forc-migrate/src/matching/typed_tree.rs +++ b/forc-plugins/forc-migrate/src/matching/typed_tree.rs @@ -4,7 +4,10 @@ use super::{any, TyElementsMatcher, TyElementsMatcherDeep, TyLocate}; use sway_ast::StorageField; use sway_core::{ decl_engine::id::DeclId, - language::ty::{TyAstNodeContent, TyDecl, TyModule, TyProgram, TyStorageDecl, TyStorageField}, + language::ty::{ + TyAstNodeContent, TyDecl, TyImplSelfOrTrait, TyModule, TyProgram, TyStorageDecl, + TyStorageField, + }, }; use sway_types::Spanned; @@ -66,6 +69,25 @@ impl TyElementsMatcherDeep for TyStorageDecl { } } +impl TyElementsMatcher> for TyModule { + fn match_elems<'a, F>( + &'a self, + predicate: F, + ) -> impl Iterator> + where + F: Fn(&&'a DeclId) -> bool + Clone + 'a, + DeclId: 'a, + { + self.all_nodes + .iter() + .filter_map(|node| match &node.content { + TyAstNodeContent::Declaration(TyDecl::ImplSelfOrTrait(decl)) => Some(&decl.decl_id), + _ => None, + }) + .filter(predicate) + } +} + impl TyLocate for TyStorageDecl { fn locate(&self, lexed_element: &StorageField) -> Option<&TyStorageField> { self.fields @@ -106,6 +128,15 @@ pub mod matchers { { scope.match_elems_deep(predicate) } + + pub(crate) fn impl_self_or_trait_decls( + scope: &S, + ) -> impl Iterator> + where + S: TyElementsMatcher>, + { + scope.match_elems(any) + } } pub mod predicates { diff --git a/forc-plugins/forc-migrate/src/migrations/demo.rs b/forc-plugins/forc-migrate/src/migrations/demo.rs index 6c48b74ab09..a359ca9a356 100644 --- a/forc-plugins/forc-migrate/src/migrations/demo.rs +++ b/forc-plugins/forc-migrate/src/migrations/demo.rs @@ -2,28 +2,38 @@ use std::vec; -use crate::migrations::{visit_lexed_modules_mut, MutProgramInfo}; -use anyhow::{Ok, Result}; -use sway_ast::{ - attribute::Annotated, - keywords::{FnToken, Keyword}, - Braces, CodeBlockContents, FnSignature, ItemFn, Module, Parens, Punctuated, +use crate::{ + internal_error, + matching::{lexed_match, lexed_match_mut, with_name, with_name_mut}, + migrations::{visit_all_modules_mut, MutProgramInfo}, + modifying::*, }; -use sway_core::Engines; -use sway_types::{Ident, Span, Spanned}; +use anyhow::{bail, Ok, Result}; +use sway_ast::Module; +use sway_core::{language::ty::TyModule, Engines}; +use sway_types::{Span, Spanned}; -use super::{DryRun, MigrationStep, MigrationStepKind}; +use super::{ContinueMigrationProcess, DryRun, MigrationStep, MigrationStepKind}; #[allow(dead_code)] pub(super) const INSERT_EMPTY_FUNCTION_STEP: MigrationStep = MigrationStep { title: "Insert `empty_function` at the end of every module", duration: 0, - kind: MigrationStepKind::CodeModification(insert_empty_function_step, &[]), + kind: MigrationStepKind::CodeModification( + insert_empty_function_step, + &[], + ContinueMigrationProcess::IfNoManualMigrationActionsNeeded, + ), help: &[ - "Migration will insert an empty function named `empty_function`", - "at the end of every module, unless the function with the same", - "name already exists in the module.", + "Migration will insert an empty function named `empty_function` at the end of", + "every module.", + " ", "E.g., `fn empty_function() {}`.", + " ", + "If a function with that name already exists in the module, it will be", + "renamed to `empty_function_old`, and a new one will be inserted.", + " ", + "If both functions already exist, the migration does not do anything.", ], }; @@ -34,87 +44,73 @@ fn insert_empty_function_step( fn insert_empty_function_step_impl( _engines: &Engines, module: &mut Module, + _ty_module: &TyModule, dry_run: DryRun, ) -> Result> { - // TODO: Simplify this demo migration by using matchers and modifiers. let mut result = vec![]; - // Code transformations must be idempotent. In this demo, if the function - // with the name `empty_function` already exists, we do not insert it. - let existing_empty_function = module - .items - .iter() - .map(|annotated| &annotated.value) - .filter_map(|decl| match decl { - sway_ast::ItemKind::Fn(module_fn) => Some(module_fn), - _ => None, - }) - .find(|module_fn| module_fn.fn_signature.name.as_str() == "empty_function"); - - if existing_empty_function.is_some() { - return Ok(result); - } + let existing_empty_function = + lexed_match::functions(module, with_name("empty_function")).next(); + let existing_empty_old_function = + lexed_match::functions(module, with_name("empty_function_old")).next(); - // If the module is empty, insert right after the module kind, - // otherwise, after the last item. - let result_span = match module.items.last() { + // If the module is empty, in the report, point at the module kind + // (`contract`, `script`, `predicate`, or `library`), otherwise, + // point at the last item. + let report_span = match module.items.last() { Some(annotated_item) => annotated_item.span(), None => module.semicolon_token.span(), }; - result.push(result_span.clone()); - - if matches!(dry_run, DryRun::Yes) { - return Ok(result); + match (existing_empty_function, existing_empty_old_function) { + (Some(_), Some(_)) => { + // Code transformations must be idempotent. In this demo, if both functions + // already exist, we don't do anything. + return Ok(vec![]); + } + (Some(_), None) => { + // `empty_function` exists, but old do not. + // Rename the existing `empty_function` to `empty_function_old`, and insert a new `empty_function`. + + // We report the occurrence of the code relevant for migration... + result.push(report_span.clone()); + + // ...and proceed with the code change only if it is not a dry-run. + if dry_run == DryRun::Yes { + return Ok(result); + } + + let Some(existing_empty_function) = + lexed_match_mut::functions(module, with_name_mut("empty_function")).next() + else { + bail!(internal_error("Existing `empty_function` cannot be found.")); + }; + + modify(existing_empty_function).set_name("empty_function_old"); + + let insert_span = Span::empty_at_end(&report_span); + let empty_function = New::function(insert_span, "empty_function"); + modify(module).append_function(empty_function); + } + (None, _) => { + // `empty_function` does not exist, create a new one. + + result.push(report_span.clone()); + + if dry_run == DryRun::Yes { + return Ok(result); + } + + let insert_span = Span::empty_at_end(&report_span); + let empty_function = New::function(insert_span, "empty_function"); + modify(module).append_function(empty_function); + } } - // Not a dry-run, proceed with the code change. - - let insert_span = Span::empty_at_end(&result_span); - - // Construct the `empty_function`. - // Note that we are using the `insert_span` for all the required spans. - let empty_function = sway_ast::ItemKind::Fn(ItemFn { - fn_signature: FnSignature { - visibility: None, - fn_token: FnToken::new(insert_span.clone()), - name: Ident::new_with_override("empty_function".into(), insert_span.clone()), - generics: None, - arguments: Parens { - inner: sway_ast::FnArgs::Static(Punctuated { - value_separator_pairs: vec![], - final_value_opt: None, - }), - span: insert_span.clone(), - }, - return_type_opt: None, - where_clause_opt: None, - }, - body: Braces { - inner: CodeBlockContents { - statements: vec![], - final_expr_opt: None, - span: insert_span.clone(), - }, - span: insert_span, - }, - }); - - // Add the constructed `empty_function` to the module items. - module.items.push(Annotated { - attribute_list: vec![], - value: empty_function, - }); - Ok(result) } - let res = visit_lexed_modules_mut( - program_info.engines, - program_info.lexed_program, - dry_run, - insert_empty_function_step_impl, - )?; + let res = visit_all_modules_mut(program_info, dry_run, insert_empty_function_step_impl)?; Ok(res.into_iter().flatten().collect()) } diff --git a/forc-plugins/forc-migrate/src/migrations/mod.rs b/forc-plugins/forc-migrate/src/migrations/mod.rs index db6fdb523f5..a4540bac6ee 100644 --- a/forc-plugins/forc-migrate/src/migrations/mod.rs +++ b/forc-plugins/forc-migrate/src/migrations/mod.rs @@ -3,29 +3,34 @@ //! //! Migration steps are defined in the submodules. Every submodule has the name //! of the corresponding breaking change Sway feature and contains all the -//! migration steps needed to migrate that feature. +//! migration steps needed to migrate to that feature. //! //! The special [demo] submodule contains demo migrations used for learning and testing //! the migration tool. mod demo; +mod partial_eq; mod references; mod storage_domains; use std::collections::HashSet; -use anyhow::Result; +use anyhow::{bail, Result}; +use duplicate::duplicate_item; +use itertools::Itertools; use sway_ast::Module; use sway_core::{ language::{ lexed::{LexedModule, LexedProgram}, - ty::TyProgram, + ty::{TyModule, TyProgram}, }, Engines, }; use sway_features::Feature; use sway_types::Span; +use crate::internal_error; + pub(crate) struct ProgramInfo<'a> { pub lexed_program: LexedProgram, pub ty_program: TyProgram, @@ -38,7 +43,6 @@ pub(crate) struct ProgramInfo<'a> { /// that modify the source code by altering the lexed program. pub(crate) struct MutProgramInfo<'a> { pub lexed_program: &'a mut LexedProgram, - #[allow(dead_code)] pub ty_program: &'a TyProgram, pub engines: &'a Engines, } @@ -103,23 +107,23 @@ impl MigrationStep { use MigrationStepExecution::*; match self.kind { MigrationStepKind::Instruction(_) => Manual, - MigrationStepKind::CodeModification(_, manual_migration_actions) + MigrationStepKind::CodeModification(_, manual_migration_actions, _) if !manual_migration_actions.is_empty() => { Semiautomatic } - MigrationStepKind::CodeModification(_, _) => Automatic, - MigrationStepKind::Interaction(_, _, _) => Semiautomatic, + MigrationStepKind::CodeModification(..) => Automatic, + MigrationStepKind::Interaction(..) => Semiautomatic, } } pub(crate) fn has_manual_actions(&self) -> bool { match self.kind { MigrationStepKind::Instruction(_) => true, - MigrationStepKind::CodeModification(_, []) => false, - MigrationStepKind::CodeModification(_, _) => true, - MigrationStepKind::Interaction(_, _, []) => false, - MigrationStepKind::Interaction(_, _, _) => true, + MigrationStepKind::CodeModification(_, [], _) => false, + MigrationStepKind::CodeModification(_, _, _) => true, + MigrationStepKind::Interaction(_, _, [], _) => false, + MigrationStepKind::Interaction(_, _, _, _) => true, } } } @@ -127,12 +131,25 @@ impl MigrationStep { /// Denotes that a migration step that changes the source code should /// be executed in a dry-run mode, means just returning the places in code /// to be changed, but without performing the actual change. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] pub(crate) enum DryRun { Yes, No, } +/// Developer's response during an interactive migration step. +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum InteractionResponse { + /// There was no interaction with the developer. + None, + /// Developer opted for executing the migration step and change the code. + ExecuteStep, + /// Developer communicated that the code change is not needed. + StepNotNeeded, + /// Developer opted for postponing the migration step. + PostponeStep, +} + /// A function that analyses a program given by the [ProgramInfo] and returns /// the [Span]s of all the places in the program code that need to be addressed /// during a manual migration step. @@ -155,14 +172,44 @@ type CodeModificationFn = for<'a> fn(&'a mut MutProgramInfo<'a>, DryRun) -> Resu /// will happen or not. /// /// Returns the [Span]s of all the places in the **original** program code that are -/// changed during the interaction. -type InteractionFn = for<'a> fn(&'a mut MutProgramInfo<'a>) -> Result>; +/// changed during the interaction, if any, together with the developer's [InteractionResponse]. +type InteractionFn = + for<'a> fn(&'a mut MutProgramInfo<'a>) -> Result<(InteractionResponse, Vec)>; + +/// A function that visits the [Module] and its corresponding [TyModule], +/// potentially alters the lexed module, and returns a +/// [Result] containing related information about the visited module. +/// +/// For its usages, see [visit_modules_mut]. +type ModuleVisitorMutFn = + for<'a> fn(&'a Engines, &'a mut Module, &'a TyModule, DryRun) -> Result; -/// A function that visits the [Module], potentially alters it, and returns a -/// [Result] containing related information about the [Module]. +/// A function that visits the [Module] and its corresponding [TyModule], +/// and returns a [Result] containing related information about the visited module. /// -/// For its usages, see [visit_lexed_modules_mut]. -type ModuleVisitorFn = for<'a> fn(&'a Engines, &'a mut Module, DryRun) -> Result; +/// For its usages, see [visit_modules]. +type ModuleVisitorFn = for<'a> fn(&'a Engines, &'a Module, &'a TyModule, DryRun) -> Result; + +/// Defines if the migration process can continue after a code modification +/// migration step. +#[derive(PartialEq, Eq, Clone, Copy)] +pub(crate) enum ContinueMigrationProcess { + /// Continue if the step has no manual migration actions specified. + /// This is the default and most common option. + IfNoManualMigrationActionsNeeded, + /// Always stop the migration. This is usually needed only after the + /// steps that represent intermediate migration to an experimental + /// feature for the purpose of early adoption. + /// + /// E.g., such step will keep the original code marked with + /// experimental feature set to false, and insert the new implementation + /// marked with experimental feature set to true. + /// + /// Continuing migration after such a step would be confusing, + /// because the next step would usually offer immediate removal of the + /// changes done in the step. + Never, +} pub(crate) enum MigrationStepKind { /// A migration step that provides instructions to developers, @@ -183,8 +230,13 @@ pub(crate) enum MigrationStepKind { /// /// **If a [MigrationStepKind::CodeModification] does not have /// _manual migration actions_ it is considered to be a fully automated migration, - /// after witch the migration process can safely continue.** - CodeModification(CodeModificationFn, &'static [&'static str]), + /// after witch the migration process can safely continue, unless marked as + /// [ContinueMigrationProcess::Never].** + CodeModification( + CodeModificationFn, + &'static [&'static str], + ContinueMigrationProcess, + ), /// A migration step that first provides instructions to developers, /// and afterwards interacts with them, giving additional instructions /// and asking for additional input. @@ -202,40 +254,118 @@ pub(crate) enum MigrationStepKind { /// /// **If a [MigrationStepKind::Interaction] does not have /// _manual migration actions_ it is considered to be finished after the interaction, - /// after witch the migration process can safely continue.** + /// after witch the migration process can safely continue, unless marked as + /// [ContinueMigrationProcess::Never].** /// /// Note that in a general case, the [InstructionFn] and the [InteractionFn] /// can return different [Span]s. E.g., during the instruction a single /// span can be returned pointing to a module in which the change needs /// to be done, while the interaction will return the actual places in the /// module that were modified. - Interaction(InstructionFn, InteractionFn, &'static [&'static str]), + Interaction( + InstructionFn, + InteractionFn, + &'static [&'static str], + ContinueMigrationProcess, + ), +} + +/// A convenient method for visiting all the modules within a program. +/// The `visitor` will be called for every module, and the method will return the +/// [Vec] containing the results of all the individual visitor calls. +pub(crate) fn visit_all_modules( + program_info: &ProgramInfo, + dry_run: DryRun, + visitor: ModuleVisitorFn, +) -> Result> { + visit_modules( + program_info.engines, + &program_info.lexed_program.root, + &program_info.ty_program.root_module, + dry_run, + visitor, + ) } -/// A convenient method for visiting all the [LexedModule]s within a [LexedProgram]. +/// A convenient method for visiting all the modules within a program. /// The `visitor` will be called for every module, and the method will return the -/// [Vec] containing the results of all the visitor calls. +/// [Vec] containing the results of all the individual visitor calls. /// -/// The `visitor` can mutate the modules. -pub(crate) fn visit_lexed_modules_mut( +/// Visitors can mutate the [LexedProgram]. +pub(crate) fn visit_all_modules_mut( + program_info: &mut MutProgramInfo, + dry_run: DryRun, + visitor: ModuleVisitorMutFn, +) -> Result> { + visit_modules_mut( + program_info.engines, + &mut program_info.lexed_program.root, + &program_info.ty_program.root_module, + dry_run, + visitor, + ) +} + +/// A convenient method for visiting the `lexed_module` and its corresponding `ty_module`, +/// and all their submodules, recursively. +/// The `visitor` will be called for every module, and the method will return the +/// [Vec] containing the results of all the individual visitor calls. +#[duplicate_item( + __visit_modules __ModuleVisitorFn __ref_type(type) __ref(value) __iter; + [visit_modules] [ModuleVisitorFn] [&type] [&value] [iter]; + [visit_modules_mut] [ModuleVisitorMutFn] [&mut type] [&mut value] [iter_mut]; +)] +pub(crate) fn __visit_modules( engines: &Engines, - lexed_program: &mut LexedProgram, + lexed_module: __ref_type([LexedModule]), + ty_module: &TyModule, dry_run: DryRun, - visitor: ModuleVisitorFn, + visitor: __ModuleVisitorFn, ) -> Result> { fn visit_modules_rec( engines: &Engines, - lexed_module: &mut LexedModule, + lexed_module: __ref_type([LexedModule]), + ty_module: &TyModule, dry_run: DryRun, - visitor: ModuleVisitorFn, + visitor: __ModuleVisitorFn, result: &mut Vec, ) -> Result<()> { - let visitor_result = visitor(engines, &mut lexed_module.tree, dry_run)?; + let visitor_result = visitor( + engines, + __ref([lexed_module.tree.value]), + ty_module, + dry_run, + )?; result.push(visitor_result); - for (_, lexed_submodule) in lexed_module.submodules.iter_mut() { + let mut lexed_submodules = lexed_module.submodules.__iter().collect_vec(); + let mut ty_submodules = ty_module.submodules.iter().collect_vec(); + + if lexed_submodules.len() != ty_submodules.len() { + bail!(internal_error(format!( + "Lexed module has \"{}\" submodules, and typed module has \"{}\" submodules.", + lexed_submodules.len(), + ty_submodules.len(), + ))); + } + + // The order of submodules is not guaranteed to be the same, hence, sorting by name to + // ensure the same ordering. + lexed_submodules.sort_by(|a, b| a.0.cmp(&b.0)); + ty_submodules.sort_by(|a, b| a.0.cmp(&b.0)); + + let lexed_submodules = lexed_submodules.__iter(); + let ty_submodules = ty_submodules.iter(); + for (lexed_submodule, ty_submodule) in lexed_submodules.zip(ty_submodules) { + if lexed_submodule.0 != ty_submodule.0 { + bail!(internal_error(format!( + "Lexed module \"{}\" does not match with the typed module \"{}\".", + lexed_submodule.0, ty_submodule.0, + ))); + } visit_modules_rec( engines, - &mut lexed_submodule.module, + __ref([lexed_submodule.1.module]), + &ty_submodule.1.module, dry_run, visitor, result, @@ -247,7 +377,8 @@ pub(crate) fn visit_lexed_modules_mut( let mut result = vec![]; visit_modules_rec( engines, - &mut lexed_program.root, + lexed_module, + ty_module, dry_run, visitor, &mut result, @@ -344,12 +475,21 @@ fn assert_migration_steps_consistency(migration_steps: MigrationSteps) { ones relevant for the next breaking change version. */ -/// The list of the migration steps, grouped by the Sway features that cause +/// The list of the migration steps, grouped by the Sway feature that causes /// the breaking changes behind the migration steps. -const MIGRATION_STEPS: MigrationSteps = &[( - Feature::StorageDomains, - &[ - self::storage_domains::REVIEW_STORAGE_SLOT_KEYS_STEP, - self::storage_domains::DEFINE_BACKWARD_COMPATIBLE_STORAGE_SLOT_KEYS_STEP, - ], -)]; +const MIGRATION_STEPS: MigrationSteps = &[ + ( + Feature::StorageDomains, + &[ + self::storage_domains::REVIEW_STORAGE_SLOT_KEYS_STEP, + self::storage_domains::DEFINE_BACKWARD_COMPATIBLE_STORAGE_SLOT_KEYS_STEP, + ], + ), + ( + Feature::PartialEq, + &[ + self::partial_eq::IMPLEMENT_EXPERIMENTAL_PARTIAL_EQ_AND_EQ_TRAITS, + self::partial_eq::REMOVE_DEPRECATED_EQ_TRAIT_IMPLEMENTATIONS, + ], + ), +]; diff --git a/forc-plugins/forc-migrate/src/migrations/partial_eq.rs b/forc-plugins/forc-migrate/src/migrations/partial_eq.rs new file mode 100644 index 00000000000..cb683da4abc --- /dev/null +++ b/forc-plugins/forc-migrate/src/migrations/partial_eq.rs @@ -0,0 +1,595 @@ +use std::vec; + +use crate::{ + internal_error, + matching::{ + item_impl, lexed_match, lexed_match_mut, literal, ty_match, with_name_mut, + LexedLocateAnnotatedMut, LexedLocateAsAnnotatedMut, + }, + migrations::{ + visit_all_modules, visit_all_modules_mut, visit_modules, InteractionResponse, + MutProgramInfo, + }, + modifying::*, + print_single_choice_menu, +}; +use anyhow::{bail, Ok, Result}; +use itertools::Itertools; +use sway_ast::{ItemKind, Module}; +use sway_core::{ + language::{ty::TyModule, CallPath}, + Engines, +}; +use sway_error::formatting::{plural_s, Indent}; +use sway_features::Feature; +use sway_types::{Ident, Span, Spanned}; + +use super::{ContinueMigrationProcess, DryRun, MigrationStep, MigrationStepKind, ProgramInfo}; + +// NOTE: We do not support cases when `Eq` is given another name via `as` alias import. +// In practice, this does not happen. + +// NOTE: We do not support cases when `Eq` is implemented locally within a function. +// In practice, this does not happen. + +// NOTE: We are searching only for standalone `#[cfg(experimental_partial_eq)]` attributes. +// For those, we can assume that they are generated using the migration tool, or that +// early adoption wasn't using complex patterns that request additional effort in +// migration steps. +// E.g., if we encounter something like: +// #[allow(dead_code), cfg(experimental_references = true, experimental_partial_eq = true)] +// we will assume that developers want to have control over the feature adoption and +// will not consider such usages in the migration. + +// NOTE: We could add an additional migration step that suggests inspecting usages of +// the `Eq` trait in trait constraints, to see if they could be replaced with `PartialEq`. +// The development effort of this additional step is questionable. We would need to extend +// visitors to collect all trait constraints, which is a considerable effort. On the other +// hand the current types that are constrained all have `Eq` semantics, which is not +// changed by the introduction of `PartialEq`. Changing `Eq` constraint to `PartialEq` +// to lower the constraint is done in the `core` and `std`, where appropriate. +// Suggesting to developers doing this replacement in their projects is mentioned +/// in the tracking issue: https://github.com/FuelLabs/sway/issues/6883. + +pub(super) const IMPLEMENT_EXPERIMENTAL_PARTIAL_EQ_AND_EQ_TRAITS: MigrationStep = MigrationStep { + title: "Implement experimental `PartialEq` and `Eq` traits", + duration: 1, + kind: MigrationStepKind::CodeModification( + implement_experimental_partial_eq_and_eq_traits, + &[], + // This is an intermediate migration for early adopting the feature. + ContinueMigrationProcess::Never, + ), + help: &[ + "Migration will implement `PartialEq` and `Eq` traits for every type", + "that implements `Eq` trait.", + " ", + "The new implementations will be marked as `#[cfg(experimental_partial_eq = true)]`.", + " ", + "The original `Eq` implementation will remain in code and be marked as", + "`#[cfg(experimental_partial_eq = false)]`.", + ], +}; + +pub(super) const REMOVE_DEPRECATED_EQ_TRAIT_IMPLEMENTATIONS: MigrationStep = MigrationStep { + title: "Remove deprecated `Eq` trait implementations and `experimental_partial_eq` attributes", + duration: 1, + kind: MigrationStepKind::Interaction( + remove_deprecated_eq_trait_implementations_instruction, + remove_deprecated_eq_trait_implementations_interaction, + &[], + ContinueMigrationProcess::IfNoManualMigrationActionsNeeded, + ), + help: &[ + "Migration will:", + " - remove deprecated `Eq` implementations.", + " - remove the `#[cfg(experimental_partial_eq = true)]` attributes from the new `Eq`", + " and `PartialEq` implementations.", + " ", + "Run this migration only if you are switching fully to the `partial_eq` feature,", + "and do not plan to use the old `Eq` implementations anymore.", + ], +}; + +const EXPERIMENTAL_PARTIAL_EQ_ATTRIBUTE: &str = "experimental_partial_eq"; + +fn remove_deprecated_eq_trait_implementations_instruction( + program_info: &ProgramInfo, +) -> Result> { + fn remove_deprecated_eq_trait_implementations_instruction_impl( + _engines: &Engines, + module: &Module, + _ty_module: &TyModule, + _dry_run: DryRun, + ) -> Result> { + let mut result = vec![]; + // Note that the typed program, depending if the `forc migrate` was run with or + // without the `partial_eq` flag, might or might not have the deprecated implementations + // represented in the typed tree. + // This is not an issue, because if, in the lexed tree, we find a trait impl of the trait + // named `Eq` that has the `#[cfg(experimental_partial_eq = false)]` attribute, this is + // enough to identify it as a deprecated `Eq` trait implementation. + + // The deprecated `Eq` implementation: + // - has the `eq` method in the body, so it must not be empty, + // - is annotated with `#[cfg(experimental_partial_eq = false)]`. + result.append(&mut find_trait_impl( + module, + "Eq", + false, + false, + ResultSpanSource::ImplTraitDefinition, + )); + + // The new `Eq` implementation: + // - has an empty impl. + // - is annotated with `#[cfg(experimental_partial_eq = true)]`. + result.append(&mut find_trait_impl( + module, + "Eq", + true, + true, + ResultSpanSource::CfgAttribute, + )); + + // The new `PartialEq` implementation: + // - has the `eq` method in the body, so it must not be empty, + // - is annotated with `#[cfg(experimental_partial_eq = true)]`. + result.append(&mut find_trait_impl( + module, + "PartialEq", + false, + true, + ResultSpanSource::CfgAttribute, + )); + + Ok(result) + } + + let res = visit_all_modules( + program_info, + DryRun::Yes, + remove_deprecated_eq_trait_implementations_instruction_impl, + )?; + + Ok(res.into_iter().flatten().collect()) +} + +fn remove_deprecated_eq_trait_implementations_interaction( + program_info: &mut MutProgramInfo, +) -> Result<(InteractionResponse, Vec)> { + /// Calculates and returns: + /// - number of deprecated `Eq` impls to remove, + /// - number of `#[cfg(experimental_partial_eq = true)]` to remove from new `Eq` impls, + /// - number of `#[cfg(experimental_partial_eq = true)]` to remove from new `PartialEq` impls. + fn calculate_number_of_code_removals( + _engines: &Engines, + module: &Module, + _ty_module: &TyModule, + _dry_run: DryRun, + ) -> Result> { + // We will conveniently reuse the `find_trait_impl` function here. + let num_of_deprecated_eq_impls = find_trait_impl( + module, + "Eq", + false, + false, + ResultSpanSource::ImplTraitDefinition, + ) + .len(); + + let num_of_cfg_attrs_on_new_eq = + find_trait_impl(module, "Eq", true, true, ResultSpanSource::CfgAttribute).len(); + + let num_of_cfg_attrs_on_new_partial_eq = find_trait_impl( + module, + "PartialEq", + false, + true, + ResultSpanSource::CfgAttribute, + ) + .len(); + + Ok(vec![( + num_of_deprecated_eq_impls, + num_of_cfg_attrs_on_new_eq, + num_of_cfg_attrs_on_new_partial_eq, + )]) + } + + let numbers_of_code_removals_per_module = visit_modules( + program_info.engines, + &program_info.lexed_program.root, + &program_info.ty_program.root_module, + DryRun::Yes, + calculate_number_of_code_removals, + )?; + + let ( + num_of_deprecated_eq_impls, + num_of_cfg_attrs_on_new_eq, + num_of_cfg_attrs_on_new_partial_eq, + ) = numbers_of_code_removals_per_module + .into_iter() + .flatten() + .fold((0, 0, 0), |acc, counts| { + (acc.0 + counts.0, acc.1 + counts.1, acc.2 + counts.2) + }); + + if num_of_deprecated_eq_impls == 0 + && num_of_cfg_attrs_on_new_eq == 0 + && num_of_cfg_attrs_on_new_partial_eq == 0 + { + return Ok((InteractionResponse::None, vec![])); + } + + println!("The following code will be removed:"); + if num_of_deprecated_eq_impls > 0 { + println!( + "{}- {} deprecated `Eq` implementation{}.", + Indent::Single, + num_of_deprecated_eq_impls, + plural_s(num_of_deprecated_eq_impls) + ); + } + if num_of_cfg_attrs_on_new_eq > 0 { + println!("{}- {} `#[cfg(experimental_partial_eq = true)]` attributes from new `Eq` implementation{}.", Indent::Single, num_of_cfg_attrs_on_new_eq, plural_s(num_of_cfg_attrs_on_new_eq)); + } + if num_of_cfg_attrs_on_new_partial_eq > 0 { + println!("{}- {} `#[cfg(experimental_partial_eq = true)]` attributes from new `PartialEq` implementation{}.", Indent::Single, num_of_cfg_attrs_on_new_partial_eq, plural_s(num_of_cfg_attrs_on_new_partial_eq)); + } + println!(); + println!("Do you want to remove that code and switch fully to the `partial_eq` feature?"); + println!(); + + if print_single_choice_menu(&[ + "Yes, remove the code and switch fully to the `partial_eq` feature.", + "No, continue using deprecated `Eq` and the new `PartialEq` and `Eq` side-by-side.", + ]) != 0 + { + return Ok((InteractionResponse::PostponeStep, vec![])); + } + + // Execute the migration step. + let mut result = vec![]; + + fn remove_deprecated_eq_impls( + _engines: &Engines, + module: &mut Module, + _ty_module: &TyModule, + _dry_run: DryRun, + ) -> Result> { + let mut spans_of_annotated_eq_impls_to_remove = vec![]; + + // Deprecated `Eq` impls must not be empty, they have the `eq` method. + let annotated_eq_impls = + lexed_match::impl_self_or_trait_decls_annotated(module).filter(|annotated| { + matches!(&annotated.value, + ItemKind::Impl(item_impl) + if item_impl::implements_trait("Eq")(&item_impl) + && !item_impl.contents.inner.is_empty() + ) + }); + + // For every empty `Eq` trait implementation... + for annotated_eq_impl in annotated_eq_impls { + // Check if the `#[cfg(experimental_partial_eq = false)]` attribute exists. + if lexed_match::cfg_attribute_standalone_single_arg( + &annotated_eq_impl.attribute_list, + EXPERIMENTAL_PARTIAL_EQ_ATTRIBUTE, + |arg| arg.as_ref().is_some_and(literal::is_bool_false), + ) + .is_none() + { + continue; + }; + + // The trait impl passes all conditions, mark it for removal. + spans_of_annotated_eq_impls_to_remove.push(annotated_eq_impl.span()); + } + + for annotated_eq_impl_span in spans_of_annotated_eq_impls_to_remove.iter() { + modify(module).remove_annotated_item(annotated_eq_impl_span); + } + + Ok(spans_of_annotated_eq_impls_to_remove) + } + + let res = visit_all_modules_mut(program_info, DryRun::No, remove_deprecated_eq_impls)?; + + result.append(&mut res.into_iter().flatten().collect()); + + fn remove_cfg_experimental_partial_eq_true_attributes( + _engines: &Engines, + module: &mut Module, + _ty_module: &TyModule, + _dry_run: DryRun, + ) -> Result> { + let mut spans_of_cfg_attributes_to_remove = vec![]; + + // Find new `Eq` and `PartialEq` impls. + let annotated_trait_impls = lexed_match_mut::impl_self_or_trait_decls_annotated(module) + .filter_map(|annotated| + if matches!(&annotated.value, + ItemKind::Impl(item_impl) + // New `Eq` impl must be empty, and `PartialEq` not, it has the `eq` method. + if item_impl::implements_trait("Eq")(&item_impl) && item_impl.contents.inner.is_empty() || + item_impl::implements_trait("PartialEq")(&item_impl) && !item_impl.contents.inner.is_empty()) + { + Some(annotated) + } else { + None + } + ) + .collect_vec(); + + // For every `Eq` and `PartialEq` trait implementation... + for annotated_trait_impl in annotated_trait_impls.iter() { + // Check if the `#[cfg(experimental_partial_eq = true)]` attribute exists. + let Some(cfg_experimental_partial_eq_attr) = + lexed_match::cfg_attribute_standalone_single_arg( + &annotated_trait_impl.attribute_list, + EXPERIMENTAL_PARTIAL_EQ_ATTRIBUTE, + |arg| arg.as_ref().is_some_and(literal::is_bool_true), + ) + else { + continue; + }; + + // The trait passes all the conditions, mark the `cfg` attribute for removal. + spans_of_cfg_attributes_to_remove.push(cfg_experimental_partial_eq_attr.span()); + } + + for annotated_trait_impl in annotated_trait_impls { + for cfg_attribute_span in spans_of_cfg_attributes_to_remove.iter() { + modify(annotated_trait_impl) + .remove_attribute_decl_containing_attribute(cfg_attribute_span); + } + } + + Ok(spans_of_cfg_attributes_to_remove) + } + + let res = visit_all_modules_mut( + program_info, + DryRun::No, + remove_cfg_experimental_partial_eq_true_attributes, + )?; + + result.append(&mut res.into_iter().flatten().collect()); + + Ok((InteractionResponse::ExecuteStep, result)) +} + +enum ResultSpanSource { + ImplTraitDefinition, + CfgAttribute, +} + +/// Searches for impls of the trait named `trait_name` within the `module`. +/// The trait impl must either be empty, or have content, depending on `is_empty_impl`. +/// The trait impl must have the `#[cfg(experimental_partial_eq)]` set to bool defined in `cfg_experimental_partial_eq`. +/// The resulting [Span] points either to the trait impl definition (without the where clause and the content), +/// or to the `cfg` attribute. +fn find_trait_impl( + module: &Module, + trait_name: &str, + is_empty_impl: bool, + cfg_experimental_partial_eq: bool, + result_span_source: ResultSpanSource, +) -> Vec { + let mut result = vec![]; + + // Find impls of the trait given by the `trait_name`. + let attributed_eq_trait_impls = lexed_match::impl_self_or_trait_decls_annotated(module) + .filter_map(|annotated| match &annotated.value { + ItemKind::Impl(item_impl) if item_impl::implements_trait(trait_name)(&item_impl) => { + Some((&annotated.attribute_list, item_impl)) + } + _ => None, + }); + + // For every trait implementation... + for (attributes, eq_trait_impl) in attributed_eq_trait_impls { + // Check if the impl body is empty or not, and same as expected. + if eq_trait_impl.contents.inner.is_empty() != is_empty_impl { + continue; + } + + // Check if the `#[cfg(experimental_partial_eq)]` attribute exists and is set to `cfg_experimental_partial_eq`. + let expected_bool_literal = if cfg_experimental_partial_eq { + literal::is_bool_true + } else { + literal::is_bool_false + }; + + let Some(cfg_experimental_partial_eq_attr) = + lexed_match::cfg_attribute_standalone_single_arg( + attributes, + EXPERIMENTAL_PARTIAL_EQ_ATTRIBUTE, + |arg| arg.as_ref().is_some_and(expected_bool_literal), + ) + else { + continue; + }; + + // The trait passes all the conditions, add it to the result. + let result_span = match result_span_source { + ResultSpanSource::ImplTraitDefinition => { + Span::join(eq_trait_impl.impl_token.span(), &eq_trait_impl.ty.span()) + } + ResultSpanSource::CfgAttribute => cfg_experimental_partial_eq_attr.span(), + }; + result.push(result_span); + } + + result +} + +fn implement_experimental_partial_eq_and_eq_traits( + program_info: &mut MutProgramInfo, + dry_run: DryRun, +) -> Result> { + fn implement_experimental_partial_eq_and_eq_traits_impl( + engines: &Engines, + lexed_module: &mut Module, + ty_module: &TyModule, + dry_run: DryRun, + ) -> Result> { + let mut result = vec![]; + + let core_ops_eq_call_path = CallPath::fullpath(&["core", "ops", "Eq"]); + + let ty_impl_traits = ty_match::impl_self_or_trait_decls(ty_module) + .map(|decl| engines.de().get_impl_self_or_trait(decl)) + .filter(|decl| decl.is_impl_trait()) + .collect_vec(); + + for ty_impl_trait in ty_impl_traits { + let implemented_trait = engines.de().get_trait( + &ty_impl_trait + .implemented_trait_decl_id() + .expect("impl is a trait impl"), + ); + // Further inspect only `core::ops::Eq` impls. + if implemented_trait.call_path != core_ops_eq_call_path { + continue; + } + + let Some((attributes, lexed_impl_eq_trait)) = + lexed_module.locate_annotated_mut(&ty_impl_trait) + else { + bail!(internal_error( + "Lexical trait implementation cannot be found." + )); + }; + + // If the impl already has `experimental_partial_eq` attribute set, we assume that the migration + // is already done for this impl. Note that we don't check if it is set to true or false. + // Just the existence of the attribute, being on the old `Eq` (false), or the new `Eq` (true), + // indicates that the `partial_eq` migrations are done for this occurrence, or that developers + // manually early adopted the feature. + if lexed_match_mut::cfg_attribute_arg( + attributes, + with_name_mut(EXPERIMENTAL_PARTIAL_EQ_ATTRIBUTE), + ) + .is_some() + { + continue; + }; + + // Check that this is the old `Eq` implementation, with the `eq` method. + // If it is the new, empty one, skip it. + if lexed_impl_eq_trait.contents.inner.is_empty() { + continue; + } + + result.push(Span::join( + lexed_impl_eq_trait.impl_token.span(), + &lexed_impl_eq_trait.ty.span(), + )); + + if dry_run == DryRun::Yes { + continue; + } + + // No dry run, perform the changes. + + // 1. Append the `cfg[experimental_partial_eq = false]` to the existing attributes. + let insert_span = if attributes.is_empty() { + Span::empty_at_start(&lexed_impl_eq_trait.span()) + } else { + Span::empty_at_end(&attributes.last().expect("attributes are not empty").span()) + }; + + let cfg_attribute_decl = New::cfg_experimental_attribute_decl( + insert_span.clone(), + Feature::PartialEq, + false, + ); + + attributes.push(cfg_attribute_decl); + + // 2. Insert the `PartialEq` and new empty `Eq` implementation. + + let Some(annotated_impl_eq_trait) = + lexed_module.locate_as_annotated_mut(&ty_impl_trait) + else { + bail!(internal_error( + "Annotated lexical trait implementation cannot be found." + )); + }; + + let mut annotated_impl_partial_eq_trait = annotated_impl_eq_trait.clone(); + + // Set the `experimental_partial_eq` attribute to true. + let Some(experimental_partial_eq_arg) = lexed_match_mut::cfg_attribute_arg( + &mut annotated_impl_partial_eq_trait.attribute_list, + with_name_mut(EXPERIMENTAL_PARTIAL_EQ_ATTRIBUTE), + ) else { + bail!(internal_error( + "Attribute \"experimental_partial_eq\" cannot be found." + )); + }; + experimental_partial_eq_arg.value = Some(New::literal_bool(insert_span, true)); + + // Define the new `Eq` trait simply by removing the content form the `PartialEq` trait. + let mut annotated_impl_new_eq_trait = annotated_impl_partial_eq_trait.clone(); + let ItemKind::Impl(impl_new_eq_trait) = &mut annotated_impl_new_eq_trait.value else { + bail!(internal_error( + "Annotated implementation of \"Eq\" trait is not an `Item::Impl`." + )); + }; + impl_new_eq_trait.contents.inner.clear(); + + // Rename the `Eq` to `PartialEq` in the new `PartialEq` trait. + let ItemKind::Impl(impl_partial_eq_trait) = &mut annotated_impl_partial_eq_trait.value + else { + bail!(internal_error( + "Annotated implementation of \"Eq\" trait is not an `Item::Impl`." + )); + }; + + let path_type_last_ident = impl_partial_eq_trait + .trait_opt + .as_mut() + .expect("impl implements `Eq` trait") + .0 + .last_segment_mut(); + path_type_last_ident.name = + Ident::new_with_override("PartialEq".into(), path_type_last_ident.name.span()); + + // If the original `Eq` impl had `Eq`s in trait constraints, replace those with `PartialEq`. + let eq_trait_constraints = + lexed_match_mut::trait_constraints(impl_partial_eq_trait, with_name_mut("Eq")); + for eq_trait_constraint in eq_trait_constraints { + let path_type_last_ident = eq_trait_constraint.last_segment_mut(); + path_type_last_ident.name = + Ident::new_with_override("PartialEq".into(), path_type_last_ident.name.span()); + } + + // Add the new trait impls to the items. + // let mut module_modifier = Modifier::new(lexed_module); + // module_modifier + modify(lexed_module) + // Inserting in reverse order so that `PartialEq` ends up before `Eq`, + // since they have the same span start which equals the span of the original `Eq`. + .insert_annotated_item_after(annotated_impl_new_eq_trait) + .insert_annotated_item_after(annotated_impl_partial_eq_trait); + + // Note that we do not need to adjust the `use` statements to include `PartialEq`. + // All `core::ops` are a part of the core's prelude. If there was a `use core::ops::Eq` + // in a modified file, it was actually not needed. + } + + Ok(result) + } + + let res = visit_all_modules_mut( + program_info, + dry_run, + implement_experimental_partial_eq_and_eq_traits_impl, + )?; + + Ok(res.into_iter().flatten().collect()) +} diff --git a/forc-plugins/forc-migrate/src/migrations/references.rs b/forc-plugins/forc-migrate/src/migrations/references.rs index 9be3432ee5d..a7a294639be 100644 --- a/forc-plugins/forc-migrate/src/migrations/references.rs +++ b/forc-plugins/forc-migrate/src/migrations/references.rs @@ -1,16 +1,16 @@ use std::vec; -use crate::migrations::{visit_lexed_modules_mut, MutProgramInfo}; +use crate::migrations::{visit_all_modules_mut, MutProgramInfo}; use anyhow::{Ok, Result}; use itertools::Itertools; use sway_ast::{ keywords::{AmpersandToken, Keyword, MutToken, Token}, Module, }; -use sway_core::Engines; +use sway_core::{language::ty::TyModule, Engines}; use sway_types::{Span, Spanned}; -use super::{DryRun, MigrationStep, MigrationStepKind}; +use super::{ContinueMigrationProcess, DryRun, MigrationStep, MigrationStepKind}; #[allow(dead_code)] pub(super) const REPLACE_REF_MUT_FN_PARAMETERS_STEP: MigrationStep = MigrationStep { @@ -22,6 +22,7 @@ pub(super) const REPLACE_REF_MUT_FN_PARAMETERS_STEP: MigrationStep = MigrationSt "change function callers, by adding `&mut` to passed parameters.", "change function bodies, by dereferencing (`*`) parameters where needed.", ], + ContinueMigrationProcess::IfNoManualMigrationActionsNeeded, ), help: &[ "Migration will replace `ref mut` function parameters with `&mut`.", @@ -48,6 +49,7 @@ fn replace_ref_mut_fn_parameters_step( fn replace_ref_mut_fn_parameters_step_impl( _engines: &Engines, module: &mut Module, + _ty_module: &TyModule, dry_run: DryRun, ) -> Result> { let mut result = vec![]; @@ -99,7 +101,7 @@ fn replace_ref_mut_fn_parameters_step( result.push(result_span); // Replace `ref mut` with `&mut` if it is not a dry-run. - if matches!(dry_run, DryRun::No) { + if dry_run == DryRun::No { *ref_opt = None; *mut_opt = None; @@ -126,13 +128,11 @@ fn replace_ref_mut_fn_parameters_step( Ok(result) } - let res = visit_lexed_modules_mut( - program_info.engines, - program_info.lexed_program, + let res = visit_all_modules_mut( + program_info, dry_run, replace_ref_mut_fn_parameters_step_impl, )?; Ok(res.into_iter().flatten().collect()) - // Ok(res) } diff --git a/forc-plugins/forc-migrate/src/migrations/storage_domains.rs b/forc-plugins/forc-migrate/src/migrations/storage_domains.rs index 470863653c9..83ca3a9e2bc 100644 --- a/forc-plugins/forc-migrate/src/migrations/storage_domains.rs +++ b/forc-plugins/forc-migrate/src/migrations/storage_domains.rs @@ -1,15 +1,15 @@ use std::collections::HashSet; -use super::{MigrationStep, MigrationStepKind, MutProgramInfo}; +use super::{ContinueMigrationProcess, MigrationStep, MigrationStepKind, MutProgramInfo}; use crate::{ internal_error, matching::{ - lexed_match, lexed_storage_field, ty_match, + lexed_match_mut, lexed_storage_field, ty_match, ty_storage_field::{with_in_keyword, without_in_keyword}, TyLocate, }, - migrations::ProgramInfo, - modifying::Modifier, + migrations::{InteractionResponse, ProgramInfo}, + modifying::*, print_single_choice_menu, }; use anyhow::{bail, Ok, Result}; @@ -18,10 +18,10 @@ use num_bigint::BigUint; use sha2::{Digest, Sha256}; use sway_core::language::{ ty::{TyExpressionVariant, TyStorageField}, - CallPath, CallPathType, Literal, + CallPath, Literal, }; use sway_error::formatting::{self, sequence_to_list}; -use sway_types::{Ident, Span, Spanned}; +use sway_types::{Span, Spanned}; pub(super) const REVIEW_STORAGE_SLOT_KEYS_STEP: MigrationStep = MigrationStep { title: "Review explicitly defined slot keys in storage declarations (`in` keywords)", @@ -46,6 +46,7 @@ pub(super) const DEFINE_BACKWARD_COMPATIBLE_STORAGE_SLOT_KEYS_STEP: MigrationSte define_backward_compatible_storage_slot_keys_step_instruction, define_backward_compatible_storage_slot_keys_step_interaction, &[], + ContinueMigrationProcess::IfNoManualMigrationActionsNeeded, ), help: &[ "If the contract owning this storage is behind a proxy, or for any other reason needs", @@ -150,10 +151,9 @@ fn define_backward_compatible_storage_slot_keys_step_instruction( fn define_backward_compatible_storage_slot_keys_step_interaction( program_info: &mut MutProgramInfo, -) -> Result> { - let mut res = vec![]; +) -> Result<(InteractionResponse, Vec)> { let Some(storage_decl_id) = ty_match::storage_decl(program_info.ty_program) else { - return Ok(res); + return Ok((InteractionResponse::None, vec![])); }; let storage_decl = &*program_info.engines.de().get_storage(&storage_decl_id); @@ -185,36 +185,40 @@ fn define_backward_compatible_storage_slot_keys_step_interaction( if print_single_choice_menu(&[ "Yes, assign the backward compatible storage slot keys.", "No, this contract does not require backwards compatibility.", - ]) == 0 + ]) != 0 { - let Some(storage_declaration) = lexed_match::storage_decl(program_info.lexed_program) - else { - bail!(internal_error( - "Lexical storage declaration cannot be found." - )); + return Ok((InteractionResponse::StepNotNeeded, vec![])); + } + + // Execute the migration step. + let mut res = vec![]; + + let Some(storage_declaration) = lexed_match_mut::storage_decl(program_info.lexed_program) + else { + bail!(internal_error( + "Lexical storage declaration cannot be found." + )); + }; + + for lexed_storage_field in lexed_match_mut::storage_fields_deep( + storage_declaration, + lexed_storage_field::without_in_keyword, + ) { + let Some(ty_storage_field) = storage_decl.locate(lexed_storage_field) else { + bail!(internal_error(format!( + "Typed storage field \"{}\" cannot be found.", + lexed_storage_field.name + ))); }; - for lexed_storage_field in lexed_match::storage_fields_deep( - storage_declaration, - lexed_storage_field::without_in_keyword, - ) { - let Some(ty_storage_field) = storage_decl.locate(lexed_storage_field) else { - bail!(internal_error(format!( - "Typed storage field \"{}\" cannot be found.", - lexed_storage_field.name - ))); - }; - - res.push(ty_storage_field.name.span()); - - let mut storage_key_modifier = Modifier::new(lexed_storage_field); - storage_key_modifier.with_in_key(BigUint::from_bytes_be( - get_previous_slot_key(ty_storage_field).as_slice(), - )); - } + res.push(ty_storage_field.name.span()); + + modify(lexed_storage_field).set_in_key(BigUint::from_bytes_be( + get_previous_slot_key(ty_storage_field).as_slice(), + )); } - Ok(res) + Ok((InteractionResponse::ExecuteStep, res)) } /// Returns storage slot keys defined in Sway Standards and Sway Libraries, @@ -240,18 +244,11 @@ fn get_well_known_slot_keys() -> HashSet { fn get_well_known_slot_keys_constants() -> HashSet { let slot_keys_constants = vec![ // For SRC14 well-known slot keys see: https://docs.fuel.network/docs/sway-libs/upgradability/#upgradability-library - ("sway_libs", "upgradability", "PROXY_OWNER_STORAGE"), - ("standards", "src14", "SRC14_TARGET_STORAGE"), + ["sway_libs", "upgradability", "PROXY_OWNER_STORAGE"], + ["standards", "src14", "SRC14_TARGET_STORAGE"], ] .into_iter() - .map(|path_parts| CallPath { - prefixes: vec![ - Ident::new_no_span(path_parts.0.into()), - Ident::new_no_span(path_parts.1.into()), - ], - suffix: Ident::new_no_span(path_parts.2.into()), - callpath_type: CallPathType::Full, - }); + .map(|path_parts| CallPath::fullpath(&path_parts)); HashSet::from_iter(slot_keys_constants) } diff --git a/forc-plugins/forc-migrate/src/modifying/annotated.rs b/forc-plugins/forc-migrate/src/modifying/annotated.rs new file mode 100644 index 00000000000..e76764812c1 --- /dev/null +++ b/forc-plugins/forc-migrate/src/modifying/annotated.rs @@ -0,0 +1,30 @@ +#[allow(unused_imports)] // Used in doc-comments. +use sway_ast::{ + attribute::{Annotated, Attribute}, + AttributeDecl, +}; + +use sway_types::{Span, Spanned}; + +use super::Modifier; + +impl<'a, T> Modifier<'a, Annotated> { + /// From `self`, removes [AttributeDecl] that contains an [Attribute] + /// whose span equals `attribute_span`. + /// + /// Method **removes the whole [AttributeDecl]**, even if it contains + /// other attributes, aside from the `attribute_span` matching one. + pub(crate) fn remove_attribute_decl_containing_attribute( + &mut self, + attribute_span: &Span, + ) -> &mut Self { + self.element.attribute_list.retain(|attribute_decl| { + attribute_decl + .attribute + .inner + .iter() + .all(|attribute| attribute.span() != *attribute_span) + }); + self + } +} diff --git a/forc-plugins/forc-migrate/src/modifying/attribute.rs b/forc-plugins/forc-migrate/src/modifying/attribute.rs new file mode 100644 index 00000000000..616f3825dfc --- /dev/null +++ b/forc-plugins/forc-migrate/src/modifying/attribute.rs @@ -0,0 +1,162 @@ +use sway_ast::{ + attribute::{Attribute, AttributeArg}, + brackets::SquareBrackets, + keywords::{HashBangToken, HashToken, Token}, + AttributeDecl, Literal, Parens, Punctuated, +}; +use sway_features::Feature; +use sway_types::{ + constants::{CFG_ATTRIBUTE_NAME, DOC_COMMENT_ATTRIBUTE_NAME}, + Ident, Span, Spanned, +}; + +use crate::assert_insert_span; + +use super::{Modifier, New}; + +#[allow(dead_code)] +impl<'a> Modifier<'a, Attribute> { + pub(crate) fn set_name + ?Sized>(&mut self, name: &S) -> &mut Self { + // We preserve the current span of the name. + let insert_span = self.element.name.span(); + self.element.name = Ident::new_with_override(name.as_ref().into(), insert_span); + + self + } +} + +#[allow(dead_code)] +impl New { + /// Creates an [Attribute] with a single [AttributeArg]. E.g. `attribute_name(arg_name = value)` or `attribute_name(arg_name)`. + pub(crate) fn attribute_with_arg + ?Sized>( + insert_span: Span, + attribute_name: &S, + arg_name: &S, + value: Option, + ) -> Attribute { + assert_insert_span!(insert_span); + + Attribute { + name: Ident::new_with_override(attribute_name.as_ref().into(), insert_span.clone()), + args: Some(Parens { + inner: Punctuated { + value_separator_pairs: vec![], + final_value_opt: Some(Box::new(AttributeArg { + name: Ident::new_with_override( + arg_name.as_ref().into(), + insert_span.clone(), + ), + value, + })), + }, + span: insert_span, + }), + } + } + + /// Creates an [AttributeDecl] with a single [Attribute] that has a single [AttributeArg]. E.g. `#[attribute_name(arg_name = value)]` or `#[attribute_name(arg_name)]`. + pub(crate) fn attribute_decl_with_arg + ?Sized>( + insert_span: Span, + attribute_name: &S, + arg_name: &S, + value: Option, + ) -> AttributeDecl { + assert_insert_span!(insert_span); + + AttributeDecl { + hash_kind: sway_ast::attribute::AttributeHashKind::Outer(HashToken::new( + insert_span.clone(), + )), + attribute: SquareBrackets { + inner: Punctuated { + value_separator_pairs: vec![], + final_value_opt: Some(Box::new(New::attribute_with_arg( + insert_span.clone(), + attribute_name, + arg_name, + value, + ))), + }, + span: insert_span, + }, + } + } + + /// Creates an [AttributeDecl] representing a single `cfg` experimental attribute. E.g. `#[cfg(experimental_flag = value)]`. + pub(crate) fn cfg_experimental_attribute_decl( + insert_span: Span, + feature: Feature, + value: bool, + ) -> AttributeDecl { + assert_insert_span!(insert_span); + + AttributeDecl { + hash_kind: sway_ast::attribute::AttributeHashKind::Outer(HashToken::new( + insert_span.clone(), + )), + attribute: SquareBrackets { + inner: Punctuated { + value_separator_pairs: vec![], + final_value_opt: Some(Box::new(New::attribute_with_arg( + insert_span.clone(), + CFG_ATTRIBUTE_NAME, + &format!("experimental_{}", feature.name()), + Some(New::literal_bool(insert_span.clone(), value)), + ))), + }, + span: insert_span, + }, + } + } + + /// Creates a `doc-comment` [AttributeDecl] that defines a single doc-comment line. + /// It automatically inserts the leading space. + /// + /// E.g., `comment` "This is a comment." will create an [AttributeDecl] that corresponds to: + /// ```ignore + /// //! This is a comment. + /// ``` + pub(crate) fn doc_comment_attribute_decl + ?Sized>( + insert_span: Span, + comment: &S, + ) -> AttributeDecl { + assert_insert_span!(insert_span); + + AttributeDecl { + hash_kind: sway_ast::attribute::AttributeHashKind::Inner(HashBangToken::new( + insert_span.clone(), + )), + attribute: SquareBrackets { + inner: Punctuated { + value_separator_pairs: vec![], + final_value_opt: Some(Box::new(New::attribute_with_arg( + insert_span.clone(), + DOC_COMMENT_ATTRIBUTE_NAME, + &format!(" {}", comment.as_ref()), + None, + ))), + }, + span: insert_span, + }, + } + } + + /// Creates `doc-comment` [AttributeDecl]s that define multiple doc-comment lines. + /// It automatically inserts the leading space into each line. + /// + /// E.g., `comments` \["This is a comment.", "This is the second line."\] will create + /// [AttributeDecl]s that corresponds to: + /// ```ignore + /// //! This is a comment. + /// //! This is the second line. + /// ``` + pub(crate) fn doc_comments_attribute_decls + ?Sized>( + insert_span: Span, + comments: &[&S], + ) -> Vec { + comments + .iter() + .map(|comment| New::doc_comment_attribute_decl(insert_span.clone(), comment)) + .collect() + } +} diff --git a/forc-plugins/forc-migrate/src/modifying/function.rs b/forc-plugins/forc-migrate/src/modifying/function.rs new file mode 100644 index 00000000000..0427e344132 --- /dev/null +++ b/forc-plugins/forc-migrate/src/modifying/function.rs @@ -0,0 +1,53 @@ +use sway_ast::{ + keywords::{FnToken, Keyword}, + Braces, CodeBlockContents, FnSignature, ItemFn, Parens, Punctuated, +}; +use sway_types::{Ident, Span, Spanned}; + +use crate::assert_insert_span; + +use super::{Modifier, New}; + +impl<'a> Modifier<'a, ItemFn> { + pub(crate) fn set_name + ?Sized>(&mut self, name: &S) -> &mut Self { + // We preserve the current span of the name. + let insert_span = self.element.fn_signature.name.span(); + self.element.fn_signature.name = + Ident::new_with_override(name.as_ref().into(), insert_span); + + self + } +} + +impl New { + /// Creates an [ItemFn] representing and empty function without arguments that is named `name`. + pub(crate) fn function + ?Sized>(insert_span: Span, name: &S) -> ItemFn { + assert_insert_span!(insert_span); + + ItemFn { + fn_signature: FnSignature { + visibility: None, + fn_token: FnToken::new(insert_span.clone()), + name: Ident::new_with_override(name.as_ref().into(), insert_span.clone()), + generics: None, + arguments: Parens { + inner: sway_ast::FnArgs::Static(Punctuated { + value_separator_pairs: vec![], + final_value_opt: None, + }), + span: insert_span.clone(), + }, + return_type_opt: None, + where_clause_opt: None, + }, + body: Braces { + inner: CodeBlockContents { + statements: vec![], + final_expr_opt: None, + span: insert_span.clone(), + }, + span: insert_span, + }, + } + } +} diff --git a/forc-plugins/forc-migrate/src/modifying/literal.rs b/forc-plugins/forc-migrate/src/modifying/literal.rs new file mode 100644 index 00000000000..bc8edc2ce00 --- /dev/null +++ b/forc-plugins/forc-migrate/src/modifying/literal.rs @@ -0,0 +1,25 @@ +use sway_ast::{ + literal::{LitBool, LitBoolType}, + Literal, +}; +use sway_types::Span; + +use crate::assert_insert_span; + +use super::New; + +impl New { + /// Creates a [Literal] representing bool `value`. + pub(crate) fn literal_bool(insert_span: Span, value: bool) -> Literal { + assert_insert_span!(insert_span); + + Literal::Bool(LitBool { + span: insert_span, + kind: if value { + LitBoolType::True + } else { + LitBoolType::False + }, + }) + } +} diff --git a/forc-plugins/forc-migrate/src/modifying/mod.rs b/forc-plugins/forc-migrate/src/modifying/mod.rs index c2d9a1d6229..31a7c9a67b2 100644 --- a/forc-plugins/forc-migrate/src/modifying/mod.rs +++ b/forc-plugins/forc-migrate/src/modifying/mod.rs @@ -1,5 +1,13 @@ -//! This module contains common API for modifying elements within a lexed tree. +//! This module contains common API for building new and modifying existing +//! elements within a lexed tree. +use sway_types::Span; + +mod annotated; +mod attribute; +mod function; +mod literal; +mod module; mod storage_field; /// A wrapper around a lexed tree element that will be modified. @@ -8,7 +16,48 @@ pub(crate) struct Modifier<'a, T> { } impl<'a, T> Modifier<'a, T> { - pub(crate) fn new(element: &'a mut T) -> Self { + // Private, so that we enforce creating modifiers with the + // `modify` function. + fn new(element: &'a mut T) -> Self { Self { element } } } + +pub(crate) fn modify(element: &mut T) -> Modifier<'_, T> { + Modifier::new(element) +} + +// Empty struct for creating new lexed elements. +// Constructors for each lexed element are in separate modules, +// grouped by lexed elements they construct, and each module +// has its own `New` impl. +pub(crate) struct New {} + +/// Trait for setting all spans within `Self` to the same insert span. +/// +/// New elements inserted into lexed tree should have their spans set +/// to the same zero-sized [Span]. This ensures that they will always +/// be properly rendered. Sometimes, new elements are copied from existing +/// elements and modified. Such new elements might not have all spans +/// set to the same, zero-sized insert span. Implementing this trait +/// ensures proper setting of the insert span. +// TODO: Implement `SetInsertSpan` for lexed tree elements. +#[allow(dead_code)] +pub(crate) trait SetInsertSpan { + fn set_insert_span(&mut self, insert_span: Span); +} + +#[macro_export] +macro_rules! assert_insert_span { + ($insert_span: ident) => { + assert!( + stringify!($insert_span) == "insert_span", + "the insert span function argument must be called `insert_span`" + ); + assert!($insert_span.is_empty(), "`insert_span` must be empty"); + assert!( + !$insert_span.is_dummy(), + "`insert_span` must not be a `Span::dummy()`" + ); + }; +} diff --git a/forc-plugins/forc-migrate/src/modifying/module.rs b/forc-plugins/forc-migrate/src/modifying/module.rs new file mode 100644 index 00000000000..76ba1fb3bf2 --- /dev/null +++ b/forc-plugins/forc-migrate/src/modifying/module.rs @@ -0,0 +1,58 @@ +use std::cmp::min; + +use sway_ast::{attribute::Annotated, ItemFn, ItemKind, Module}; + +use sway_types::{Span, Spanned}; + +use super::Modifier; + +#[allow(dead_code)] +impl<'a> Modifier<'a, Module> { + /// Removes an [Annotated] from `self`. + /// The item to remove is identified by its [Span], `annotated_item_span`. + pub(crate) fn remove_annotated_item(&mut self, annotated_item_span: &Span) -> &mut Self { + self.element + .items + .retain(|annotated| annotated.span() != *annotated_item_span); + self + } + + /// Inserts `annotated_item` after the first already existing item whose [Span::end] + /// is greater than or equal to `annotated_item`'s [Span::start]. + pub(crate) fn insert_annotated_item_after( + &mut self, + annotated_item: Annotated, + ) -> &mut Self { + let first_existing_preceding_item_index = self + .element + .items + .iter() + .position(|annotated| annotated.span().end() >= annotated_item.span().start()) + .unwrap_or(0) + + 1; + let index = min( + first_existing_preceding_item_index, + self.element.items.len(), + ); + self.element.items.insert(index, annotated_item); + + self + } + + /// Appends `annotated_item` at the end of the [Module]. + pub(crate) fn append_annotated_item( + &mut self, + annotated_item: Annotated, + ) -> &mut Self { + self.element.items.push(annotated_item); + self + } + + pub(crate) fn append_function(&mut self, function: ItemFn) -> &mut Self { + let function = Annotated { + attribute_list: vec![], + value: ItemKind::Fn(function), + }; + self.append_annotated_item(function) + } +} diff --git a/forc-plugins/forc-migrate/src/modifying/storage_field.rs b/forc-plugins/forc-migrate/src/modifying/storage_field.rs index 52a218c2f09..6cccd322f8c 100644 --- a/forc-plugins/forc-migrate/src/modifying/storage_field.rs +++ b/forc-plugins/forc-migrate/src/modifying/storage_field.rs @@ -34,8 +34,9 @@ impl ToInKey for Expr { } } -impl Modifier<'_, StorageField> { - pub(crate) fn with_in_key(&mut self, key: K) -> &mut Self { +#[allow(dead_code)] +impl<'a> Modifier<'a, StorageField> { + pub(crate) fn set_in_key(&mut self, key: K) -> &mut Self { // If the `in` token already exists, just replace the key and leave the `in` // token as is. Place the key after the `in` token. let insert_span = if let Some(in_token) = &self.element.in_token { @@ -54,8 +55,7 @@ impl Modifier<'_, StorageField> { self } - #[allow(dead_code)] - pub(crate) fn without_in_key(&mut self) -> &mut Self { + pub(crate) fn remove_in_key(&mut self) -> &mut Self { self.element.in_token = None; self.element.key_expr = None; diff --git a/scripts/mdbook-forc-documenter/examples/forc_migrate.md b/scripts/mdbook-forc-documenter/examples/forc_migrate.md index d1f4972e002..a05bdd1dc93 100644 --- a/scripts/mdbook-forc-documenter/examples/forc_migrate.md +++ b/scripts/mdbook-forc-documenter/examples/forc_migrate.md @@ -177,10 +177,14 @@ At the end of the `run`, the migration will either guide you to: - `Continue` the migration process by performing the manual actions and re-running the `forc migrate run` afterwards, - or will mark the migration process as `Finished`. At this point, your project will be compatible with the next breaking change version of Sway. +`forc migrate`, same like `forc fmt`, does its best to preserve the positions of comments in the modified code. This is a challenging task, especially if migration steps remove parts of the code. **It is a good practice to always `diff` the changes done within migration steps and check if the comments are placed where expected.** + ## Migrating workspaces To migrate a workspace, you will need to migrate each workspace member separately, following the above procedure. The projects should be migrated in order of their dependencies. +> **Note**: There is a know limitation when running `forc migrate` on projects that are listed as workspace members. `forc migrate` will run, but possibly not find all the occurrences in code that need to be migrated. Therefore, **before running migrations on projects that are workspace members, remove them temporarily from the the list of workspace `members`**. + ## Additional after-migration steps There are some additional manual steps that might be needed after the migration. diff --git a/sway-ast/src/item/item_trait.rs b/sway-ast/src/item/item_trait.rs index 7d1d345c6f4..d74ffe33c35 100644 --- a/sway-ast/src/item/item_trait.rs +++ b/sway-ast/src/item/item_trait.rs @@ -67,6 +67,20 @@ pub struct Traits { pub suffixes: Vec<(AddToken, PathType)>, } +impl Traits { + pub fn iter(&self) -> impl Iterator { + vec![&self.prefix] + .into_iter() + .chain(self.suffixes.iter().map(|(_add_token, path)| path)) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + vec![&mut self.prefix] + .into_iter() + .chain(self.suffixes.iter_mut().map(|(_add_token, path)| path)) + } +} + impl Spanned for Traits { fn span(&self) -> Span { match self.suffixes.last() { diff --git a/sway-ast/src/path.rs b/sway-ast/src/path.rs index c7be1225e87..ac73ffa5bdd 100644 --- a/sway-ast/src/path.rs +++ b/sway-ast/src/path.rs @@ -72,6 +72,14 @@ impl PathType { .last() .unwrap_or(&self.prefix) } + + pub fn last_segment_mut(&mut self) -> &mut PathTypeSegment { + self.suffix + .iter_mut() + .map(|s| &mut s.1) + .last() + .unwrap_or(&mut self.prefix) + } } impl Spanned for PathType { diff --git a/sway-core/src/language/lexed/mod.rs b/sway-core/src/language/lexed/mod.rs index 4ca08a948a8..6c5d73ca86a 100644 --- a/sway-core/src/language/lexed/mod.rs +++ b/sway-core/src/language/lexed/mod.rs @@ -2,7 +2,7 @@ mod program; use crate::language::ModName; pub use program::LexedProgram; -use sway_ast::Module; +use sway_ast::{attribute::Annotated, Module}; use super::{HasModule, HasSubmodules}; @@ -10,7 +10,7 @@ use super::{HasModule, HasSubmodules}; #[derive(Debug, Clone)] pub struct LexedModule { /// The content of this module in the form of a [Module]. - pub tree: Module, + pub tree: Annotated, /// Submodules introduced within this module using the `dep` syntax in order of declaration. pub submodules: Vec<(ModName, LexedSubmodule)>, } diff --git a/sway-core/src/language/parsed/expression/mod.rs b/sway-core/src/language/parsed/expression/mod.rs index d6f6f2df54b..5cf36a3776e 100644 --- a/sway-core/src/language/parsed/expression/mod.rs +++ b/sway-core/src/language/parsed/expression/mod.rs @@ -627,8 +627,8 @@ pub(crate) struct Op { } impl Op { - pub fn to_var_name(&self) -> Ident { - Ident::new_with_override(self.op_variant.as_str().to_string(), self.span.clone()) + pub fn to_method_name(&self) -> Ident { + Ident::new_with_override(self.op_variant.method_name().to_string(), self.span.clone()) } } @@ -653,7 +653,14 @@ pub enum OpVariant { } impl OpVariant { - fn as_str(&self) -> &'static str { + /// For all the operators except [OpVariant::Or] and [OpVariant::And], + /// returns the name of the method that can be found on the corresponding + /// operator trait. E.g., for `+` that will be the method `add` defined in + /// `core::ops::Add::add`. + /// + /// [OpVariant::Or] and [OpVariant::And] are lazy and must be handled + /// internally by the compiler. + fn method_name(&self) -> &'static str { use OpVariant::*; match self { Add => "add", diff --git a/sway-core/src/language/ty/declaration/impl_trait.rs b/sway-core/src/language/ty/declaration/impl_trait.rs index 6125eeb9569..0e617cfac65 100644 --- a/sway-core/src/language/ty/declaration/impl_trait.rs +++ b/sway-core/src/language/ty/declaration/impl_trait.rs @@ -1,6 +1,6 @@ -use super::{TyDeclParsedType, TyTraitItem}; +use super::{TyAbiDecl, TyDeclParsedType, TyTraitDecl, TyTraitItem}; use crate::{ - decl_engine::DeclRefMixedInterface, + decl_engine::{DeclId, DeclRefMixedInterface, InterfaceDeclId}, engine_threading::*, has_changes, language::{parsed::ImplSelfOrTrait, CallPath}, @@ -29,6 +29,46 @@ impl TyImplSelfOrTrait { pub fn is_impl_contract(&self, te: &TypeEngine) -> bool { matches!(&*te.get(self.implementing_for.type_id), TypeInfo::Contract) } + + pub fn is_impl_self(&self) -> bool { + self.trait_decl_ref.is_none() + } + + pub fn is_impl_trait(&self) -> bool { + match &self.trait_decl_ref { + Some(decl_ref) => matches!(decl_ref.id(), InterfaceDeclId::Trait(_)), + _ => false, + } + } + + pub fn is_impl_abi(&self) -> bool { + match &self.trait_decl_ref { + Some(decl_ref) => matches!(decl_ref.id(), InterfaceDeclId::Abi(_)), + _ => false, + } + } + + /// Returns [DeclId] of the trait implemented by `self`, if `self` implements a trait. + pub fn implemented_trait_decl_id(&self) -> Option> { + match &self.trait_decl_ref { + Some(decl_ref) => match &decl_ref.id() { + InterfaceDeclId::Trait(decl_id) => Some(*decl_id), + InterfaceDeclId::Abi(_) => None, + }, + _ => None, + } + } + + /// Returns [DeclId] of the ABI implemented by `self`, if `self` implements an ABI for a contract. + pub fn implemented_abi_decl_id(&self) -> Option> { + match &self.trait_decl_ref { + Some(decl_ref) => match &decl_ref.id() { + InterfaceDeclId::Abi(decl_id) => Some(*decl_id), + InterfaceDeclId::Trait(_) => None, + }, + _ => None, + } + } } impl TyDeclParsedType for TyImplSelfOrTrait { diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 322e1ae6660..8cc6c3ac6c2 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -223,7 +223,7 @@ fn parse_in_memory( let lexed_program = lexed::LexedProgram::new( kind, lexed::LexedModule { - tree: module.value, + tree: module, submodules: Vec::default(), }, ); @@ -375,7 +375,7 @@ fn parse_module_tree( .map(|s| (s.name.clone(), s.lexed.clone())) .collect::>(); let lexed = lexed::LexedModule { - tree: module.value, + tree: module, submodules: lexed_submodules, }; diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index ee3a0ccf63a..955fea5c445 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -60,12 +60,8 @@ impl ty::TyExpression { arguments: Vec, span: Span, ) -> Result { - let engines = ctx.engines; - let ctx = ctx.with_type_annotation(engines.te().insert( - engines, - TypeInfo::Boolean, - span.source_id(), - )); + let type_engine = ctx.engines.te(); + let ctx = ctx.with_type_annotation(type_engine.id_of_bool()); Self::core_ops(handler, ctx, OpVariant::Equals, arguments, span) } @@ -75,12 +71,8 @@ impl ty::TyExpression { arguments: Vec, span: Span, ) -> Result { - let engines = ctx.engines; - let ctx = ctx.with_type_annotation(engines.te().insert( - engines, - TypeInfo::Boolean, - span.source_id(), - )); + let type_engine = ctx.engines.te(); + let ctx = ctx.with_type_annotation(type_engine.id_of_bool()); Self::core_ops(handler, ctx, OpVariant::NotEquals, arguments, span) } @@ -102,7 +94,7 @@ impl ty::TyExpression { op_variant, span: span.clone(), } - .to_var_name(), + .to_method_name(), callpath_type: CallPathType::Full, }; let mut method_name_binding = TypeBinding { diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index fb68d4e6ca6..297a4cc706f 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -782,14 +782,15 @@ impl<'a> TypeCheckContext<'a> { Ok(matching_item_decl_refs) } - /// Given a name and a type (plus a `self_type` to potentially - /// resolve it), find that method in the namespace. Requires `args_buf` - /// because of some special casing for the standard library where we pull - /// the type from the arguments buffer. + /// Given a `method_name` and a `type_id`, find that method on that type in the namespace. + /// `annotation_type` is the expected method return type. Requires `argument_types` because: + /// - standard operations like +, <=, etc. are called like "core::ops::" and the + /// actual self type of the trait implementation is determined by the passed argument type. + /// - we can have several implementations of generic traits for different types, that can + /// result in a method of a same name, but with different type arguments. /// - /// This function will generate a missing method error if the method is not - /// found. - #[allow(clippy::too_many_arguments)] // TODO: remove lint bypass once private modules are no longer experimental + /// This function will emit a [CompileError::MethodNotFound] if the method is not found. + #[allow(clippy::too_many_arguments)] pub(crate) fn find_method_for_type( &mut self, handler: &Handler, diff --git a/sway-features/src/lib.rs b/sway-features/src/lib.rs index 953827a30e5..eb0c4343240 100644 --- a/sway-features/src/lib.rs +++ b/sway-features/src/lib.rs @@ -157,6 +157,8 @@ features! { "https://github.com/FuelLabs/sway/issues/6701", references = true, "https://github.com/FuelLabs/sway/issues/5063", + partial_eq = false, + "https://github.com/FuelLabs/sway/issues/6883", } #[derive(Clone, Debug, Default, Parser)] diff --git a/sway-lib-core/src/codec.sw b/sway-lib-core/src/codec.sw index 913da976a0c..9901f40d5c9 100644 --- a/sway-lib-core/src/codec.sw +++ b/sway-lib-core/src/codec.sw @@ -5110,6 +5110,7 @@ where } } +#[cfg(experimental_partial_eq = false)] fn assert_encoding_and_decoding( value: T, expected: SLICE, @@ -5151,6 +5152,48 @@ where } } +#[cfg(experimental_partial_eq = true)] +fn assert_encoding_and_decoding( + value: T, + expected: SLICE, +) +where + T: PartialEq + AbiEncode + AbiDecode, +{ + let len = __size_of::(); + + if len == 0 { + __revert(0); + } + + let expected = raw_slice::from_parts::(__addr_of(expected), len); + let actual = encode(value); + + if actual.len::() != expected.len::() { + __revert(0); + } + + let result = asm( + result, + expected: expected.ptr(), + actual: actual.ptr(), + len: len, + ) { + meq result expected actual len; + result: bool + }; + + if !result { + __revert(0); + } + + let decoded = abi_decode::(actual); + __log(decoded); + if !decoded.eq(value) { + __revert(0); + } +} + fn to_slice(array: T) -> raw_slice { let len = __size_of::(); raw_slice::from_parts::(__addr_of(array), len) @@ -5165,6 +5208,7 @@ where } } +#[cfg(experimental_partial_eq = false)] fn assert_eq(a: T, b: T, revert_code: u64) where T: Eq, @@ -5174,6 +5218,17 @@ where } } +#[cfg(experimental_partial_eq = true)] +fn assert_eq(a: T, b: T, revert_code: u64) +where + T: PartialEq, +{ + if a != b { + __revert(revert_code) + } +} + +#[cfg(experimental_partial_eq = false)] fn assert_neq(a: T, b: T, revert_code: u64) where T: Eq, @@ -5183,6 +5238,16 @@ where } } +#[cfg(experimental_partial_eq = true)] +fn assert_neq(a: T, b: T, revert_code: u64) +where + T: PartialEq, +{ + if a == b { + __revert(revert_code) + } +} + fn assert_no_write_after_buffer(value_to_append: T, size_of_t: u64) where T: AbiEncode, diff --git a/sway-lib-core/src/never.sw b/sway-lib-core/src/never.sw index af47b9c5222..3ddecf9ccb2 100644 --- a/sway-lib-core/src/never.sw +++ b/sway-lib-core/src/never.sw @@ -1,7 +1,11 @@ library; +#[cfg(experimental_partial_eq = false)] use ::ops::{Eq, Not, Ord}; +#[cfg(experimental_partial_eq = true)] +use ::ops::{Eq, Not, Ord, PartialEq}; + /// `!` represents the type of computations which never resolve to any value at all. /// /// # Additional Information @@ -59,12 +63,23 @@ impl Not for ! { } } +#[cfg(experimental_partial_eq = false)] impl Eq for ! { fn eq(self, _other: Self) -> bool { self } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for ! { + fn eq(self, _other: Self) -> bool { + self + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for ! {} + impl Ord for ! { fn gt(self, _other: Self) -> bool { self diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index c49147bccae..cc01cf146fc 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -529,6 +529,7 @@ impl Not for u8 { } /// Trait to evaluate if two types are equal. +#[cfg(experimental_partial_eq = false)] pub trait Eq { /// Evaluates if two values of the same type are equal. /// @@ -601,54 +602,247 @@ pub trait Eq { } } +/// Trait for comparing type instances corresponding to equivalence relations. +/// +/// The difference between [Eq] and [PartialEq] is the additional requirement for reflexivity. +/// [PartialEq] guarantees symmetry and transitivity, but not reflexivity. +/// +/// E.g., a type that implements [PartialEq] guarantees that for all `a`, `b`, and `c`: +/// - `a == b` implies `b == a` (symmetry) +/// - `a == b` and `b == c` implies `a == c` (transitivity) +/// +/// [Eq], additionally implies: +/// - `a == a` for every `a` (reflexivity) +/// +/// Reflexivity property cannot be checked by the compiler, and therefore `Eq` +/// does not have any methods, but only [PartialEq] as a supertrait. +/// +/// **Implementing [Eq] for a type that does not have reflexivity property is a logic error**. +#[cfg(experimental_partial_eq = true)] +pub trait Eq: PartialEq { +} + +/// Trait for comparing type instances using the equality operator. +/// +/// Implementing this trait provides `==` and `!=` operators on a type. +/// +/// This trait allows comparisons for types that do not have a full equivalence relation. +/// In other words, it is not required that each instance of the type must be +/// equal to itself. While most of the types used in blockchain development do have this +/// property, called reflexivity, we can encounter types that are not reflexive. +/// +/// A typical example of a type supporting partial equivalence, but not equivalence, +/// is a floating point number, where `NaN` is different from any other number, +/// including itself: `NaN != NaN`. +#[cfg(experimental_partial_eq = true)] +pub trait PartialEq { + /// Evaluates if two values of the same type are equal. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if the values are equal, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl PartialEq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 2 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result = struct1 == struct2; + /// assert(result); + /// } + /// ``` + fn eq(self, other: Self) -> bool; +} { + /// Evaluates if two values of the same type are not equal. + /// + /// # Additional Information + /// + /// This function is inherited when `eq()` is implemented. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if the two values are not equal, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl PartialEq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 2 }; + /// let result = struct1 != struct2; + /// assert(result); + /// } + /// ``` + fn neq(self, other: Self) -> bool { + (self.eq(other)).not() + } +} + +#[cfg(experimental_partial_eq = false)] impl Eq for bool { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for bool { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for bool {} + +#[cfg(experimental_partial_eq = false)] impl Eq for u256 { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for u256 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for u256 {} + +#[cfg(experimental_partial_eq = false)] impl Eq for b256 { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for b256 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for b256 {} + +#[cfg(experimental_partial_eq = false)] impl Eq for u64 { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for u64 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for u64 {} + +#[cfg(experimental_partial_eq = false)] impl Eq for u32 { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for u32 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for u32 {} + +#[cfg(experimental_partial_eq = false)] impl Eq for u16 { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for u16 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for u16 {} + +#[cfg(experimental_partial_eq = false)] impl Eq for u8 { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for u8 { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for u8 {} + +#[cfg(experimental_partial_eq = false)] impl Eq for raw_ptr { fn eq(self, other: Self) -> bool { __eq(self, other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_ptr { + fn eq(self, other: Self) -> bool { + __eq(self, other) + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_ptr {} + /// Trait to evaluate if one value is greater or less than another of the same type. pub trait Ord { /// Evaluates if one value of the same type is greater than another. @@ -995,6 +1189,7 @@ impl BitwiseXor for u8 { } /// Trait to evaluate if one value is greater than or equal, or less than or equal to another of the same type. +#[cfg(experimental_partial_eq = false)] pub trait OrdEq: Ord + Eq { } { /// Evaluates if one value of the same type is greater or equal to than another. @@ -1089,6 +1284,101 @@ pub trait OrdEq: Ord + Eq { } } +#[cfg(experimental_partial_eq = true)] +pub trait OrdEq: Ord + PartialEq { +} { + /// Evaluates if one value of the same type is greater or equal to than another. + /// + /// # Additional Information + /// + /// This trait requires that the `Ord` and `Eq` traits are implemented. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if `self` is greater than or equal to `other`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Eq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// impl Ord for MyStruct { + /// fn gt(self, other: Self) -> bool { + /// self.val > other.val + /// } + /// } + /// + /// impl OrdEq for MyStruct {} + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 10 }; + /// let result = struct1 >= struct2; + /// assert(result); + /// } + /// ``` + fn ge(self, other: Self) -> bool { + self.gt(other) || self.eq(other) + } + /// Evaluates if one value of the same type is less or equal to than another. + /// + /// # Additional Information + /// + /// This trait requires that the `Ord` and `Eq` traits are implemented. + /// + /// # Arguments + /// + /// * `other`: [Self] - The value of the same type. + /// + /// # Returns + /// + /// * [bool] - `true` if `self` is less than or equal to `other`, otherwise `false`. + /// + /// # Examples + /// + /// ```sway + /// struct MyStruct { + /// val: u64, + /// } + /// + /// impl Eq for MyStruct { + /// fn eq(self, other: Self) -> bool { + /// self.val == other.val + /// } + /// } + /// + /// impl Ord for MyStruct { + /// fn lt(self, other: Self) -> bool { + /// self.val < other.val + /// } + /// } + /// + /// impl OrdEq for MyStruct {} + /// + /// fn foo() { + /// let struct1 = MyStruct { val: 10 }; + /// let struct2 = MyStruct { val: 10 }; + /// let result = struct1 <= struct2; + /// assert(result); + /// } + /// ``` + fn le(self, other: Self) -> bool { + self.lt(other) || self.eq(other) + } +} + impl OrdEq for u256 {} impl OrdEq for u64 {} impl OrdEq for u32 {} @@ -1383,6 +1673,7 @@ fn test_decompose() { use ::str::*; +#[cfg(experimental_partial_eq = false)] impl Eq for str { fn eq(self, other: Self) -> bool { if self.len() != other.len() { @@ -1399,6 +1690,26 @@ impl Eq for str { } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + false + } else { + let self_ptr = self.as_ptr(); + let other_ptr = other.as_ptr(); + let l = self.len(); + asm(r1: self_ptr, r2: other_ptr, r3: l, r4) { + meq r4 r1 r2 r3; + r4: bool + } + } + } +} + +#[cfg(experimental_partial_eq = true)] +impl Eq for str {} + fn assert(v: bool) { if !v { __revert(0) diff --git a/sway-lib-std/src/address.sw b/sway-lib-std/src/address.sw index 3a55037b51d..eb9b7cbc4fb 100644 --- a/sway-lib-std/src/address.sw +++ b/sway-lib-std/src/address.sw @@ -70,11 +70,20 @@ impl Address { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Address { fn eq(self, other: Self) -> bool { self.bits == other.bits } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Address { + fn eq(self, other: Self) -> bool { + self.bits == other.bits + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Address {} /// Functions for casting between the `b256` and `Address` types. impl From for Address { diff --git a/sway-lib-std/src/asset_id.sw b/sway-lib-std/src/asset_id.sw index 4cd2b8feae6..20a0ca313bb 100644 --- a/sway-lib-std/src/asset_id.sw +++ b/sway-lib-std/src/asset_id.sw @@ -29,11 +29,20 @@ impl Hash for AssetId { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for AssetId { fn eq(self, other: Self) -> bool { self.bits == other.bits } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for AssetId { + fn eq(self, other: Self) -> bool { + self.bits == other.bits + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for AssetId {} impl From for AssetId { /// Casts raw `b256` data to an `AssetId`. diff --git a/sway-lib-std/src/b512.sw b/sway-lib-std/src/b512.sw index a6673741c5a..6e0ffd25959 100644 --- a/sway-lib-std/src/b512.sw +++ b/sway-lib-std/src/b512.sw @@ -12,11 +12,20 @@ pub struct B512 { bits: [b256; 2], } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for B512 { fn eq(self, other: Self) -> bool { (self.bits)[0] == (other.bits)[0] && (self.bits)[1] == (other.bits)[1] } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for B512 { + fn eq(self, other: Self) -> bool { + (self.bits)[0] == (other.bits)[0] && (self.bits)[1] == (other.bits)[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for B512 {} impl From<(b256, b256)> for B512 { /// Converts from a `b256` tuple to a `B512`. diff --git a/sway-lib-std/src/bytes.sw b/sway-lib-std/src/bytes.sw index 76075df80ce..820998c2940 100644 --- a/sway-lib-std/src/bytes.sw +++ b/sway-lib-std/src/bytes.sw @@ -873,6 +873,7 @@ impl Bytes { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Bytes { fn eq(self, other: Self) -> bool { if self.len != other.len { @@ -885,6 +886,21 @@ impl core::ops::Eq for Bytes { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Bytes { + fn eq(self, other: Self) -> bool { + if self.len != other.len { + return false; + } + + asm(result, r2: self.buf.ptr, r3: other.buf.ptr, r4: self.len) { + meq result r2 r3 r4; + result: bool + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Bytes {} impl AsRawSlice for Bytes { /// Returns a raw slice of all of the elements in the type. diff --git a/sway-lib-std/src/contract_id.sw b/sway-lib-std/src/contract_id.sw index 2763fa92a82..a85b2786227 100644 --- a/sway-lib-std/src/contract_id.sw +++ b/sway-lib-std/src/contract_id.sw @@ -32,11 +32,20 @@ impl ContractId { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for ContractId { fn eq(self, other: Self) -> bool { self.bits == other.bits } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for ContractId { + fn eq(self, other: Self) -> bool { + self.bits == other.bits + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for ContractId {} impl From for ContractId { /// Casts raw `b256` data to a `ContractId`. diff --git a/sway-lib-std/src/identity.sw b/sway-lib-std/src/identity.sw index 7c3b06bf5e9..65de7e8ace0 100644 --- a/sway-lib-std/src/identity.sw +++ b/sway-lib-std/src/identity.sw @@ -19,6 +19,7 @@ pub enum Identity { } // ANCHOR_END: docs_identity +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Identity { fn eq(self, other: Self) -> bool { match (self, other) { @@ -28,6 +29,18 @@ impl core::ops::Eq for Identity { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Identity { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Identity::Address(addr1), Identity::Address(addr2)) => addr1 == addr2, + (Identity::ContractId(id1), Identity::ContractId(id2)) => id1 == id2, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Identity {} impl Identity { /// Returns the `Address` of the `Identity`. diff --git a/sway-lib-std/src/inputs.sw b/sway-lib-std/src/inputs.sw index f4db2869bb2..d7338325184 100644 --- a/sway-lib-std/src/inputs.sw +++ b/sway-lib-std/src/inputs.sw @@ -57,6 +57,7 @@ pub enum Input { Message: (), } +#[cfg(experimental_partial_eq = false)] impl Eq for Input { fn eq(self, other: Self) -> bool { match (self, other) { @@ -67,6 +68,19 @@ impl Eq for Input { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Input { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Input::Coin, Input::Coin) => true, + (Input::Contract, Input::Contract) => true, + (Input::Message, Input::Message) => true, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Input {} // General Inputs diff --git a/sway-lib-std/src/option.sw b/sway-lib-std/src/option.sw index 882879cd3b6..d5fe373741e 100644 --- a/sway-lib-std/src/option.sw +++ b/sway-lib-std/src/option.sw @@ -88,6 +88,7 @@ pub enum Option { } // ANCHOR_END: docs_option +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Option where T: Eq, @@ -100,6 +101,24 @@ where } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Option +where + T: PartialEq, +{ + fn eq(self, other: Self) -> bool { + match (self, other) { + (Option::Some(a), Option::Some(b)) => a == b, + (Option::None, Option::None) => true, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Option +where + T: Eq, +{} // Type implementation // diff --git a/sway-lib-std/src/outputs.sw b/sway-lib-std/src/outputs.sw index 527a94de2fc..63d005aac6d 100644 --- a/sway-lib-std/src/outputs.sw +++ b/sway-lib-std/src/outputs.sw @@ -275,6 +275,7 @@ pub fn output_asset_to(index: u64) -> Option

{ } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Output { fn eq(self, other: Self) -> bool { match (self, other) { @@ -287,3 +288,18 @@ impl core::ops::Eq for Output { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Output { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Output::Coin, Output::Coin) => true, + (Output::Contract, Output::Contract) => true, + (Output::Change, Output::Change) => true, + (Output::Variable, Output::Variable) => true, + (Output::ContractCreated, Output::ContractCreated) => true, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Output {} diff --git a/sway-lib-std/src/result.sw b/sway-lib-std/src/result.sw index 9b0532ee567..d9a8122d0e9 100644 --- a/sway-lib-std/src/result.sw +++ b/sway-lib-std/src/result.sw @@ -270,6 +270,7 @@ impl Result { // - `err(self) -> Option` } +#[cfg(experimental_partial_eq = false)] impl Eq for Result where T: Eq, @@ -283,3 +284,23 @@ where } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Result +where + T: PartialEq, + E: PartialEq, +{ + fn eq(self, other: Self) -> bool { + match (self, other) { + (Self::Ok(a), Self::Ok(b)) => a == b, + (Self::Err(a), Self::Err(b)) => a == b, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Result +where + T: Eq, + E: Eq, +{} diff --git a/sway-lib-std/src/string.sw b/sway-lib-std/src/string.sw index b6015a756bc..acaae4f618b 100644 --- a/sway-lib-std/src/string.sw +++ b/sway-lib-std/src/string.sw @@ -317,11 +317,20 @@ impl From for raw_slice { } } +#[cfg(experimental_partial_eq = false)] impl Eq for String { fn eq(self, other: Self) -> bool { self.bytes == other.as_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for String { + fn eq(self, other: Self) -> bool { + self.bytes == other.as_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for String {} impl Hash for String { fn hash(self, ref mut state: Hasher) { diff --git a/sway-lib-std/src/tx.sw b/sway-lib-std/src/tx.sw index 033b937ec50..873647247df 100644 --- a/sway-lib-std/src/tx.sw +++ b/sway-lib-std/src/tx.sw @@ -62,6 +62,7 @@ pub enum Transaction { Blob: (), } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Transaction { fn eq(self, other: Self) -> bool { match (self, other) { @@ -75,6 +76,22 @@ impl core::ops::Eq for Transaction { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Transaction { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Transaction::Script, Transaction::Script) => true, + (Transaction::Create, Transaction::Create) => true, + (Transaction::Mint, Transaction::Mint) => true, + (Transaction::Upgrade, Transaction::Upgrade) => true, + (Transaction::Upload, Transaction::Upload) => true, + (Transaction::Blob, Transaction::Blob) => true, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Transaction {} /// Get the type of the current transaction. /// diff --git a/sway-lib-std/src/u128.sw b/sway-lib-std/src/u128.sw index ef016cfe560..8bb53376064 100644 --- a/sway-lib-std/src/u128.sw +++ b/sway-lib-std/src/u128.sw @@ -144,11 +144,20 @@ impl From for (u64, u64) { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for U128 { fn eq(self, other: Self) -> bool { self.lower == other.lower && self.upper == other.upper } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for U128 { + fn eq(self, other: Self) -> bool { + self.lower == other.lower && self.upper == other.upper + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for U128 {} impl core::ops::Ord for U128 { fn gt(self, other: Self) -> bool { diff --git a/sway-lib-std/src/vm/evm/evm_address.sw b/sway-lib-std/src/vm/evm/evm_address.sw index f3b03e0692a..2b64b74ed7f 100644 --- a/sway-lib-std/src/vm/evm/evm_address.sw +++ b/sway-lib-std/src/vm/evm/evm_address.sw @@ -77,11 +77,20 @@ impl EvmAddress { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for EvmAddress { fn eq(self, other: Self) -> bool { self.bits == other.bits } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for EvmAddress { + fn eq(self, other: Self) -> bool { + self.bits == other.bits + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for EvmAddress {} /// Functions for casting between the `b256` and `EvmAddress` types. impl From for EvmAddress { diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 4576238ae9a..ef2d5ae1667 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -382,7 +382,7 @@ pub fn traverse( // Convert the source_id to a path so we can use the manifest path to get the program_id. // This is used to store the metrics for the module. - if let Some(source_id) = lexed.root.tree.span().source_id() { + if let Some(source_id) = lexed.root.tree.value.span().source_id() { let path = engines.se().get_path(source_id); let program_id = program_id_from_path(&path, engines)?; session.metrics.insert(program_id, metrics); @@ -535,13 +535,14 @@ pub fn parse_lexed_program( lexed_program .root .tree + .value .items .iter() .chain( lexed_program .root .submodules_recursive() - .flat_map(|(_, submodule)| &submodule.module.tree.items), + .flat_map(|(_, submodule)| &submodule.module.tree.value.items), ) .filter(should_process) .collect::>() diff --git a/sway-lsp/src/traverse/lexed_tree.rs b/sway-lsp/src/traverse/lexed_tree.rs index fdce32d7e9a..0c2e2b94c75 100644 --- a/sway-lsp/src/traverse/lexed_tree.rs +++ b/sway-lsp/src/traverse/lexed_tree.rs @@ -29,12 +29,12 @@ impl<'a> LexedTree<'a> { } pub fn collect_module_kinds(&self, lexed_program: &LexedProgram) { - insert_module_kind(self.ctx, &lexed_program.root.tree.kind); + insert_module_kind(self.ctx, &lexed_program.root.tree.value.kind); lexed_program .root .submodules_recursive() .for_each(|(_, dep)| { - insert_module_kind(self.ctx, &dep.module.tree.kind); + insert_module_kind(self.ctx, &dep.module.tree.value.kind); }); } } diff --git a/swayfmt/src/utils/language/expr/mod.rs b/swayfmt/src/utils/language/expr/mod.rs index 95a53c01c30..1246eb9f905 100644 --- a/swayfmt/src/utils/language/expr/mod.rs +++ b/swayfmt/src/utils/language/expr/mod.rs @@ -418,11 +418,34 @@ impl Format for Expr { mut_token, expr, } => { - write!(formatted_code, "{}", AmpersandToken::AS_STR)?; - if mut_token.is_some() { - write!(formatted_code, "{} ", MutToken::AS_STR)?; + // TODO: Currently, the parser does not support taking + // references on references without spaces between + // ampersands. E.g., `&&&x` is not supported and must + // be written as `& & &x`. + // See: https://github.com/FuelLabs/sway/issues/6808 + // Until this issue is fixed, we need this workaround + // in case of referenced expression `expr` being itself a + // reference. + if !matches!(expr.as_ref(), Self::Ref { .. }) { + // TODO: Keep this code once the issue is fixed. + write!(formatted_code, "{}", AmpersandToken::AS_STR)?; + if mut_token.is_some() { + write!(formatted_code, "{} ", MutToken::AS_STR)?; + } + expr.format(formatted_code, formatter)?; + } else { + // TODO: This is the workaround if `expr` is a reference. + write!(formatted_code, "{}", AmpersandToken::AS_STR)?; + // If we have the `mut`, we will also + // get a space after it, so the next `&` + // will be separated. Otherwise, insert space. + if mut_token.is_some() { + write!(formatted_code, "{} ", MutToken::AS_STR)?; + } else { + write!(formatted_code, " ")?; + } + expr.format(formatted_code, formatter)?; } - expr.format(formatted_code, formatter)?; } Self::Deref { star_token: _, diff --git a/swayfmt/src/utils/language/ty.rs b/swayfmt/src/utils/language/ty.rs index 327ed8165cc..9afcd12ffae 100644 --- a/swayfmt/src/utils/language/ty.rs +++ b/swayfmt/src/utils/language/ty.rs @@ -132,17 +132,49 @@ fn format_ref( mut_token: &Option, ty: &Ty, ) -> Result<(), FormatterError> { - write!( - formatted_code, - "{}{}", - AmpersandToken::AS_STR, - if mut_token.is_some() { - format!("{} ", MutToken::AS_STR) - } else { - "".to_string() - }, - )?; - ty.format(formatted_code, formatter)?; + // TODO: Currently, the parser does not support declaring + // references on references without spaces between + // ampersands. E.g., `&&&T` is not supported and must + // be written as `& & &T`. + // See: https://github.com/FuelLabs/sway/issues/6808 + // Until this issue is fixed, we need this workaround + // in case of referenced type `ty` being itself a + // reference. + if !matches!(ty, Ty::Ref { .. }) { + // TODO: Keep this code once the issue is fixed. + write!( + formatted_code, + "{}{}", + AmpersandToken::AS_STR, + if mut_token.is_some() { + format!("{} ", MutToken::AS_STR) + } else { + "".to_string() + }, + )?; + ty.format(formatted_code, formatter)?; + } else { + // TODO: This is the workaround if `ty` is a reference. + write!( + formatted_code, + "{}{}{}", + AmpersandToken::AS_STR, + if mut_token.is_some() { + format!("{} ", MutToken::AS_STR) + } else { + "".to_string() + }, + // If we have the `mut`, we will also + // get a space after it, so the next `&` + // will be separated. Otherwise, insert space. + if mut_token.is_some() { + "".to_string() + } else { + " ".to_string() + }, + )?; + ty.format(formatted_code, formatter)?; + } Ok(()) } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap index 993f90f8407..125c77486fc 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/stdout.snap @@ -11,7 +11,7 @@ warning | 1 | predicate; 2 | #[cfg(c)] a - | --- Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" or "experimental_storage_domains" or "experimental_references" + | --- Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" or "experimental_storage_domains" or "experimental_references" or "experimental_partial_eq" | ____ diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/json_abi_oracle.json deleted file mode 100644 index fe51488c706..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/json_abi_oracle.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/src/main.sw index 4e40cf402b3..81077caed02 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/src/main.sw @@ -522,6 +522,7 @@ fn f() -> bool { // Check that return path analysis is applied to local impl methods. +#[cfg(experimental_partial_eq = false)] fn g() -> bool { struct X { @@ -544,6 +545,30 @@ fn g() -> bool { x == y } +// Check that return path analysis is applied to local impl methods. +#[cfg(experimental_partial_eq = true)] +fn g() -> bool { + + struct X { + y: bool, + } + + impl core::ops::PartialEq for X { + fn eq(self, other: Self) -> bool { + if true { + return true; + } else { + return false; + }; + } + } + + let x = X { y : false }; + let y = X { y : true } ; + + x == y +} + // Check that return path analysis is applied to local functions. // Local functions are currently not supported, but once they are added this test should fail for // the same reason as for the local impl in the function g(). diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/test.partial_eq.toml new file mode 100644 index 00000000000..6a988afc24b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/return_path_analysis/test.partial_eq.toml @@ -0,0 +1,11 @@ +category = "fail" + +experimental = { new_encoding = true, partial_eq = true } + +# check: $()Declaring nested functions is currently not implemented. +# check: $()Could not find symbol "tester" in this scope. +# check: $()This path must return a value of type "bool" from function "f", but it does not. +# check: $()This path must return a value of type "bool" from function "eq", but it does not. +# check: $()Aborting due to 4 errors. + + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/src/main.sw index 86916e65b14..2fdc8202a25 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/src/main.sw @@ -1,4 +1,6 @@ script; + +#[cfg(experimental_partial_eq = false)] fn main() -> u64 { let a = 255; @@ -15,6 +17,31 @@ fn main() -> u64 { } } + if X::Y(true) == X::Y(true) { + a + } else { + a + } +} + +#[cfg(experimental_partial_eq = true)] +fn main() -> u64 { + let a = 255; + + enum X { + Y: bool, + } + + impl core::ops::PartialEq for X { + fn eq(self, other: Self) -> bool { + asm(r1: self, r2: other, r3) { + eq r3 r2 r1; + r3: bool + } + } + } + impl core::ops::Eq for X {} + if X::Y(true) == X::Y(true) { a } else { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/test.partial_eq.toml new file mode 100644 index 00000000000..7a007dd0164 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/dca/trait_method_neq/test.partial_eq.toml @@ -0,0 +1,5 @@ +category = "compile" +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json similarity index 95% rename from test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle_new_encoding.json rename to test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json index 2daeaffdc6a..d8c52d54b8c 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle_new_encoding.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json @@ -62,82 +62,82 @@ { "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "name": "BOOL", - "offset": 6896 + "offset": 6640 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "U8", - "offset": 7088 + "offset": 6832 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "ANOTHER_U8", - "offset": 6824 + "offset": 6568 }, { "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", "name": "U16", - "offset": 7032 + "offset": 6776 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U32", - "offset": 7072 + "offset": 6816 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U64", - "offset": 7080 + "offset": 6824 }, { "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", "name": "U256", - "offset": 7040 + "offset": 6784 }, { "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", "name": "B256", - "offset": 6864 + "offset": 6608 }, { "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", "name": "CONFIGURABLE_STRUCT", - "offset": 6984 + "offset": 6728 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_A", - "offset": 6904 + "offset": 6648 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_B", - "offset": 6944 + "offset": 6688 }, { "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", "name": "ARRAY_BOOL", - "offset": 6832 + "offset": 6576 }, { "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", "name": "ARRAY_U64", - "offset": 6840 + "offset": 6584 }, { "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", "name": "TUPLE_BOOL_U64", - "offset": 7016 + "offset": 6760 }, { "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", "name": "STR_4", - "offset": 7008 + "offset": 6752 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "NOT_USED", - "offset": 7000 + "offset": 6744 } ], "encodingVersion": "1", diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/src/main.sw index c89257f717b..8e7e921b90e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/src/main.sw @@ -10,9 +10,10 @@ struct ConfigurableStruct { enum ConfigurableEnum { A: bool, B: u64, - C: b256 + C: b256, } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for ConfigurableEnum { fn eq(self, other: ConfigurableEnum) -> bool { match (self, other) { @@ -22,6 +23,18 @@ impl core::ops::Eq for ConfigurableEnum { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for ConfigurableEnum { + fn eq(self, other: ConfigurableEnum) -> bool { + match (self, other) { + (ConfigurableEnum::A(inner1), ConfigurableEnum::A(inner2)) => inner1 == inner2, + (ConfigurableEnum::B(inner1), ConfigurableEnum::B(inner2)) => inner1 == inner2, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for ConfigurableEnum {} type AnotherU8 = u8; @@ -41,8 +54,7 @@ configurable { ARRAY_U64: [u64; 3] = [9, 8, 7], TUPLE_BOOL_U64: (bool, u64) = (true, 11), STR_4: str[4] = __to_str_array("abcd"), - - NOT_USED: u8 = 1 + NOT_USED: u8 = 1, } fn main() { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/test.partial_eq.toml new file mode 100644 index 00000000000..830c883841a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/test.partial_eq.toml @@ -0,0 +1,23 @@ +category = "run" +expected_result = { action = "return_data", value = "" } +validate_abi = true +expected_warnings = 1 +unsupported_profiles = ["debug"] + +experimental = { new_encoding = true, partial_eq = true } + +# check: $()ANOTHER_U8 8 +# check: $()ARRAY_BOOL 8 +# check: $()ARRAY_U64 24 +# check: $()B256 32 +# check: $()BOOL 8 +# check: $()CONFIGURABLE_ENUM_A 40 +# check: $()CONFIGURABLE_ENUM_B 40 +# check: $()CONFIGURABLE_STRUCT 16 +# check: $()NOT_USED 8 +# check: $()STR_4 8 +# check: $()TUPLE_BOOL_U64 16 +# check: $()U16 8 +# check: $()U256 32 +# check: $()U32 8 +# check: $()U64 8 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json deleted file mode 100644 index 096b1d487c9..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/json_abi_oracle.json +++ /dev/null @@ -1,284 +0,0 @@ -{ - "configurables": [ - { - "configurableType": { - "name": "", - "type": 5, - "typeArguments": null - }, - "name": "BOOL", - "offset": 3392 - }, - { - "configurableType": { - "name": "", - "type": 13, - "typeArguments": null - }, - "name": "U8", - "offset": 3528 - }, - { - "configurableType": { - "name": "", - "type": 13, - "typeArguments": null - }, - "name": "ANOTHER_U8", - "offset": 3320 - }, - { - "configurableType": { - "name": "", - "type": 9, - "typeArguments": null - }, - "name": "U16", - "offset": 3472 - }, - { - "configurableType": { - "name": "", - "type": 11, - "typeArguments": null - }, - "name": "U32", - "offset": 3512 - }, - { - "configurableType": { - "name": "", - "type": 11, - "typeArguments": null - }, - "name": "U64", - "offset": 3520 - }, - { - "configurableType": { - "name": "", - "type": 10, - "typeArguments": null - }, - "name": "U256", - "offset": 3480 - }, - { - "configurableType": { - "name": "", - "type": 4, - "typeArguments": null - }, - "name": "B256", - "offset": 3360 - }, - { - "configurableType": { - "name": "", - "type": 8, - "typeArguments": [] - }, - "name": "CONFIGURABLE_STRUCT", - "offset": 3432 - }, - { - "configurableType": { - "name": "", - "type": 6, - "typeArguments": [] - }, - "name": "CONFIGURABLE_ENUM_A", - "offset": 3400 - }, - { - "configurableType": { - "name": "", - "type": 6, - "typeArguments": [] - }, - "name": "CONFIGURABLE_ENUM_B", - "offset": 3416 - }, - { - "configurableType": { - "name": "", - "type": 2, - "typeArguments": null - }, - "name": "ARRAY_BOOL", - "offset": 3328 - }, - { - "configurableType": { - "name": "", - "type": 3, - "typeArguments": null - }, - "name": "ARRAY_U64", - "offset": 3336 - }, - { - "configurableType": { - "name": "", - "type": 1, - "typeArguments": null - }, - "name": "TUPLE_BOOL_U64", - "offset": 3456 - }, - { - "configurableType": { - "name": "", - "type": 7, - "typeArguments": null - }, - "name": "STR_4", - "offset": 3448 - } - ], - "functions": [ - { - "attributes": null, - "inputs": [], - "name": "main", - "output": { - "name": "", - "type": 0, - "typeArguments": null - } - } - ], - "loggedTypes": [], - "messagesTypes": [], - "types": [ - { - "components": [], - "type": "()", - "typeId": 0, - "typeParameters": null - }, - { - "components": [ - { - "name": "__tuple_element", - "type": 5, - "typeArguments": null - }, - { - "name": "__tuple_element", - "type": 12, - "typeArguments": null - } - ], - "type": "(_, _)", - "typeId": 1, - "typeParameters": null - }, - { - "components": [ - { - "name": "__array_element", - "type": 5, - "typeArguments": null - } - ], - "type": "[_; 3]", - "typeId": 2, - "typeParameters": null - }, - { - "components": [ - { - "name": "__array_element", - "type": 12, - "typeArguments": null - } - ], - "type": "[_; 3]", - "typeId": 3, - "typeParameters": null - }, - { - "components": null, - "type": "b256", - "typeId": 4, - "typeParameters": null - }, - { - "components": null, - "type": "bool", - "typeId": 5, - "typeParameters": null - }, - { - "components": [ - { - "name": "A", - "type": 5, - "typeArguments": null - }, - { - "name": "B", - "type": 12, - "typeArguments": null - } - ], - "type": "enum ConfigurableEnum", - "typeId": 6, - "typeParameters": null - }, - { - "components": null, - "type": "str[4]", - "typeId": 7, - "typeParameters": null - }, - { - "components": [ - { - "name": "a", - "type": 5, - "typeArguments": null - }, - { - "name": "b", - "type": 12, - "typeArguments": null - } - ], - "type": "struct ConfigurableStruct", - "typeId": 8, - "typeParameters": null - }, - { - "components": null, - "type": "u16", - "typeId": 9, - "typeParameters": null - }, - { - "components": null, - "type": "u256", - "typeId": 10, - "typeParameters": null - }, - { - "components": null, - "type": "u32", - "typeId": 11, - "typeParameters": null - }, - { - "components": null, - "type": "u64", - "typeId": 12, - "typeParameters": null - }, - { - "components": null, - "type": "u8", - "typeId": 13, - "typeParameters": null - } - ] -} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw index a0e5d11a345..aa76db9f2f4 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/src/main.sw @@ -10,9 +10,10 @@ struct ConfigurableStruct { enum ConfigurableEnum { A: bool, B: u64, - C: b256 + C: b256, } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for ConfigurableEnum { fn eq(self, other: ConfigurableEnum) -> bool { match (self, other) { @@ -22,6 +23,18 @@ impl core::ops::Eq for ConfigurableEnum { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for ConfigurableEnum { + fn eq(self, other: ConfigurableEnum) -> bool { + match (self, other) { + (ConfigurableEnum::A(inner1), ConfigurableEnum::A(inner2)) => inner1 == inner2, + (ConfigurableEnum::B(inner1), ConfigurableEnum::B(inner2)) => inner1 == inner2, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for ConfigurableEnum {} type AnotherU8 = u8; @@ -41,12 +54,10 @@ configurable { ARRAY_U64: [u64; 3] = [9, 8, 7], TUPLE_BOOL_U64: (bool, u64) = (true, 11), STR_4: str[4] = __to_str_array("abcd"), - - NOT_USED: u8 = 1 + NOT_USED: u8 = 1, } -fn main() { -} +fn main() {} #[test] fn t() { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.partial_eq.toml new file mode 100644 index 00000000000..8d1f085d8f6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_tests/test.partial_eq.toml @@ -0,0 +1,3 @@ +category = "unit_tests_pass" + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.json index ad50b55d54c..15d130af949 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.json @@ -2,7 +2,14 @@ "configurables": [], "functions": [ { - "attributes": null, + "attributes": [ + { + "arguments": [ + "experimental_partial_eq" + ], + "name": "cfg" + } + ], "inputs": [], "name": "main", "output": { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..069a79b053f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle.partial_eq.json @@ -0,0 +1,30 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": [ + { + "arguments": [ + "experimental_partial_eq" + ], + "name": "cfg" + } + ], + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle_new_encoding.json index 05b0f04b2d3..069a79b053f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle_new_encoding.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/json_abi_oracle_new_encoding.json @@ -9,7 +9,14 @@ "encodingVersion": "1", "functions": [ { - "attributes": null, + "attributes": [ + { + "arguments": [ + "experimental_partial_eq" + ], + "name": "cfg" + } + ], "inputs": [], "name": "main", "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/src/main.sw index 3534fec7fff..e43b99c828d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/src/main.sw @@ -1,4 +1,6 @@ script; + +#[cfg(experimental_partial_eq = false)] fn main() -> u64 { let a = 255; @@ -35,3 +37,42 @@ fn main() -> u64 { a } } + +#[cfg(experimental_partial_eq = true)] +fn main() -> u64 { + let a = 255; + + enum X { + Y: bool, + } + + impl core::ops::PartialEq for X { + fn eq(self, other: Self) -> bool { + asm(r1: self, r2: other, r3) { + eq r3 r2 r1; + r3: bool + } + } + } + impl core::ops::Eq for X {} + + impl core::ops::Ord for X { + fn lt(self, other: Self) -> bool { + asm(r1: self, r2: other, r3) { + lt r3 r2 r1; + r3: bool + } + } + fn gt(self, other: Self) -> bool { + asm(r1: self, r2: other, r3) { + gt r3 r2 r1; + r3: bool + } + } + } + if X::Y(true) == X::Y(true) { + a + } else { + a + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/test.partial_eq.toml new file mode 100644 index 00000000000..1e150f39f9c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "00000000000000FF" } +validate_abi = true +expected_warnings = 3 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/src/main.sw index 4df22003a57..d6417aebf76 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/src/main.sw @@ -8,134 +8,172 @@ use traits::*; use traits::nested_traits::*; struct Data { - x: T + x: T, } impl Data { - fn contains(self, other: T) -> bool where T: Eq { - self.x == other - } + fn contains(self, other: T) -> bool + where + T: Eq, + { + self.x == other + } } struct Data2 { - x: T, - y: K + x: T, + y: K, } -impl Data2 { - fn contains(self, other: Self) -> bool where T: Eq, K: Eq { - self.x == other.x && self.y == other.y - } - fn contains2(self, other: Self) -> bool where T: Eq, K: Eq { - self.x == other.x && self.y == other.y - } +impl Data2 { + fn contains(self, other: Self) -> bool + where + T: Eq, + K: Eq, + { + self.x == other.x && self.y == other.y + } + fn contains2(self, other: Self) -> bool + where + T: Eq, + K: Eq, + { + self.x == other.x && self.y == other.y + } } -impl Data2 { - fn contains3(self, other: Self) -> bool where T: Eq, K: Eq { - self.x == other.x && self.y == other.y - } - - fn contains4(first: Self, second: Self) -> bool where T: Eq, K: Eq { - first.x == second.x && first.y == second.y - } - - fn contains5(self, other: Self) -> bool where T: MyEq, K: MyEq { - self.x.my_eq(other.x) && self.y.my_eq(other.y) - } - - fn contains6(self, other: Self) -> bool where T: MyEq2, K: MyEq2 { - self.x.my_eq2(other.x) && self.y.my_eq2(other.y) - } +impl Data2 { + fn contains3(self, other: Self) -> bool + where + T: Eq, + K: Eq, + { + self.x == other.x && self.y == other.y + } + + fn contains4(first: Self, second: Self) -> bool + where + T: Eq, + K: Eq, + { + first.x == second.x && first.y == second.y + } + + fn contains5(self, other: Self) -> bool + where + T: MyEq, + K: MyEq, + { + self.x.my_eq(other.x) && self.y.my_eq(other.y) + } + + fn contains6(self, other: Self) -> bool + where + T: MyEq2, + K: MyEq2, + { + self.x.my_eq2(other.x) && self.y.my_eq2(other.y) + } } struct Data3 { - x: u64 + x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Data3 { - fn eq(self, other: Self) -> bool { - self.x == other.x - } + fn eq(self, other: Self) -> bool { + self.x == other.x + } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Data3 { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Data3 {} impl MyEq for Data3 { - fn my_eq(self, other: Self) -> bool { - self.x == other.x - } + fn my_eq(self, other: Self) -> bool { + self.x == other.x + } } // This tests it is still possible to call generic methods // where its parent has constraints // https://github.com/FuelLabs/sway/issues/6384 -trait MyTrait { } -struct S { } +trait MyTrait { +} +struct S {} struct M1 {} -impl MyTrait for M1 { } +impl MyTrait for M1 {} -impl S where T: MyTrait { - fn bar() { - } +impl S +where + T: MyTrait, +{ + fn bar() {} } fn issue_6384() { - let _x = S::::bar::(); + let _x = S::::bar::(); } fn main() -> u64 { - let s = Data { x: 42 }; - assert(s.contains(42)); - assert(!s.contains(41)); + let s = Data { x: 42 }; + assert(s.contains(42)); + assert(!s.contains(41)); + let d = Data2 { x: 42, y: 42 }; + assert(d.contains(Data2 { x: 42, y: 42 })); + assert(!d.contains(Data2 { x: 42, y: 41 })); + assert(!d.contains(Data2 { x: 41, y: 42 })); - let d = Data2 { x: 42, y: 42 }; - assert(d.contains(Data2 { x: 42, y: 42 })); - assert(!d.contains(Data2 { x: 42, y: 41 })); - assert(!d.contains(Data2 { x: 41, y: 42 })); + assert(d.contains2(Data2 { x: 42, y: 42 })); + assert(!d.contains2(Data2 { x: 42, y: 41 })); + assert(!d.contains2(Data2 { x: 41, y: 42 })); - assert(d.contains2(Data2 { x: 42, y: 42 })); - assert(!d.contains2(Data2 { x: 42, y: 41 })); - assert(!d.contains2(Data2 { x: 41, y: 42 })); + assert(d.contains3(Data2 { x: 42, y: 42 })); + assert(!d.contains3(Data2 { x: 42, y: 41 })); + assert(!d.contains3(Data2 { x: 41, y: 42 })); - assert(d.contains3(Data2 { x: 42, y: 42 })); - assert(!d.contains3(Data2 { x: 42, y: 41 })); - assert(!d.contains3(Data2 { x: 41, y: 42 })); + assert(Data2::contains4(d, Data2 { x: 42, y: 42 })); + assert(!Data2::contains4(d, Data2 { x: 42, y: 41 })); + assert(!Data2::contains4(d, Data2 { x: 41, y: 42 })); - assert(Data2::contains4(d, Data2 { x: 42, y: 42 })); - assert(!Data2::contains4(d, Data2 { x: 42, y: 41 })); - assert(!Data2::contains4(d, Data2 { x: 41, y: 42 })); + assert(d.contains5(Data2 { x: 42, y: 42 })); + assert(!d.contains5(Data2 { x: 42, y: 41 })); + assert(!d.contains5(Data2 { x: 41, y: 42 })); - assert(d.contains5(Data2 { x: 42, y: 42 })); - assert(!d.contains5(Data2 { x: 42, y: 41 })); - assert(!d.contains5(Data2 { x: 41, y: 42 })); + assert(d.contains6(Data2 { x: 42, y: 42 })); + assert(!d.contains6(Data2 { x: 42, y: 41 })); + assert(!d.contains6(Data2 { x: 41, y: 42 })); - assert(d.contains6(Data2 { x: 42, y: 42 })); - assert(!d.contains6(Data2 { x: 42, y: 41 })); - assert(!d.contains6(Data2 { x: 41, y: 42 })); - -// TODO (Esdrubal): activate test once #3818 is merged. -/* + // TODO (Esdrubal): activate test once #3818 is merged. + /* let d2 = Data2 { x: 42, y: true }; assert(d2.contains5(Data2 { x: 42, y: true })); assert(!d2.contains5(Data2 { x: 42, y: false })); assert(!d2.contains5(Data2 { x: 41, y: true })); */ - - let d3 = Data { x: Data3 { x: 42 }}; - assert(d3.contains(Data3 { x: 42 })); - assert(!d3.contains(Data3 { x: 41 })); - -// TODO (Esdrubal): activate test once #3818 is merged. -/* + let d3 = Data { + x: Data3 { x: 42 }, + }; + assert(d3.contains(Data3 { x: 42 })); + assert(!d3.contains(Data3 { x: 41 })); + + // TODO (Esdrubal): activate test once #3818 is merged. + /* let d4 = Data2 { x: 42, y: Data3 { x: 42 } }; assert(d4.contains5(Data2 { x: 42, y: Data3 { x: 42 } })); assert(!d4.contains5(Data2 { x: 42, y: Data3 { x: 41 } })); assert(!d4.contains5(Data2 { x: 41, y: Data3 { x: 42 } })); */ + issue_6384(); - issue_6384(); - - 10 + 10 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/test.partial_eq.toml.todo new file mode 100644 index 00000000000..e3b2efd4794 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self_where/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6899 is fixed. +# Compilation fails with error: No method "contains4(Data2, Data2) -> bool" found for type "Data2". +category = "run" +expected_result = { action = "return_data", value = "000000000000000A" } +validate_abi = true + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..ea0a762cd2d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "type": "u32" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/src/main.sw index e5fd95985b4..626d6f63782 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/src/main.sw @@ -18,6 +18,7 @@ enum PrimaryColor { Blue: (), } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for PrimaryColor { fn eq(self, other: Self) -> bool { asm(r1: self, r2: other, r3) { @@ -26,6 +27,17 @@ impl core::ops::Eq for PrimaryColor { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for PrimaryColor { + fn eq(self, other: Self) -> bool { + asm(r1: self, r2: other, r3) { + eq r3 r1 r2; + r3: bool + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for PrimaryColor {} impl core::ops::Ord for PrimaryColor { fn lt(self, other: Self) -> bool { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/test.partial_eq.toml new file mode 100644 index 00000000000..bcb1acc557c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "0000000A" } +validate_abi = true +expected_warnings = 6 + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..05b0f04b2d3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/asset.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/asset.sw index 592d6e67e21..c4e5ee6f60e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/asset.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/asset.sw @@ -3,11 +3,20 @@ library; use core::ops::Eq; pub struct Asset { - pub value: u64 + pub value: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Asset { fn eq(self, other: Self) -> bool { self.value == other.value } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Asset { + fn eq(self, other: Self) -> bool { + self.value == other.value + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Asset {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/utils.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/utils.sw index 9902fe206cb..a75e9a55a7a 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/utils.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/src/utils.sw @@ -4,21 +4,28 @@ use core::ops::Eq; use ::asset::Asset; pub struct Wrapper { - pub asset: Asset + pub asset: Asset, } impl Wrapper { pub fn new(value: u64) -> Self { Wrapper { - asset: Asset { - value - } + asset: Asset { value }, } } } +#[cfg(experimental_partial_eq = false)] impl Eq for Wrapper { fn eq(self, other: Self) -> bool { self.asset == other.asset } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Wrapper { + fn eq(self, other: Self) -> bool { + self.asset == other.asset + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Wrapper {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/test.partial_eq.toml new file mode 100644 index 00000000000..2748a4c0973 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/test.partial_eq.toml @@ -0,0 +1,4 @@ +category = "run" +expected_result = { action = "return_data", value = "000000000000000A" } +validate_abi = true +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..aab161b0506 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "type": "()" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/src/eq_impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/src/eq_impls.sw index edc4563ac49..9bfdd59e3fa 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/src/eq_impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/src/eq_impls.sw @@ -3,6 +3,7 @@ library; use ::data_structures::{SomeEnum, SomeStruct}; use core::ops::Eq; +#[cfg(experimental_partial_eq = false)] impl Eq for SomeEnum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -12,13 +13,35 @@ impl Eq for SomeEnum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for SomeEnum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (SomeEnum::A(val), SomeEnum::A(other_val)) => { + val == other_val + }, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for SomeEnum {} +#[cfg(experimental_partial_eq = false)] impl Eq for SomeStruct { fn eq(self, other: Self) -> bool { self.a == other.a } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for SomeStruct { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for SomeStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for Vec> { fn eq(self, other: Self) -> bool { if self.len() != other.len() { @@ -34,7 +57,26 @@ impl Eq for Vec> { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Vec> { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Vec> {} +#[cfg(experimental_partial_eq = false)] impl Eq for Vec> { fn eq(self, other: Self) -> bool { if self.len() != other.len() { @@ -51,3 +93,23 @@ impl Eq for Vec> { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Vec> { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Vec> { +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/test.partial_eq.toml new file mode 100644 index 00000000000..0005918c99d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_with_different_callpaths/test.partial_eq.toml @@ -0,0 +1,5 @@ +category = "run" +expected_result = { action = "return_data", value = "" } +validate_abi = true + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/.gitignore b/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/.gitignore deleted file mode 100644 index 77d3844f58c..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -target diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..05b0f04b2d3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/src/main.sw index 7768500da96..7d454d76a87 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/src/main.sw @@ -1,5 +1,6 @@ script; +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for [u64; 2] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -12,7 +13,23 @@ impl core::ops::Eq for [u64; 2] { true } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 2 { + if self[i] != other[i] { + return false; + } + i += 1; + } + true + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for [u64; 2] {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Vec<[u64; 2]> { fn eq(self, other: Self) -> bool { if self.len() != other.len() { @@ -28,6 +45,24 @@ impl core::ops::Eq for Vec<[u64; 2]> { true } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Vec<[u64; 2]> { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Vec<[u64; 2]> {} fn tester1(arg: Vec<[u64; 2]>) { let mut expected = Vec::new(); @@ -46,19 +81,18 @@ fn tester2(arg: Vec<[u64; 2]>) { } fn main() -> u64 { + let mut arg1 = Vec::new(); + arg1.push([0, 1]); + arg1.push([0, 1]); + tester1(arg1); - let mut arg1 = Vec::new(); - arg1.push([0, 1]); - arg1.push([0, 1]); - tester1(arg1); - - let mut arg2 = Vec::new(); - arg2.push([0, 1]); - arg2.push([0, 2]); - tester2(arg2); + let mut arg2 = Vec::new(); + arg2.push([0, 1]); + arg2.push([0, 2]); + tester2(arg2); - arg1.push([0, 1]); - tester2(arg1); + arg1.push([0, 1]); + tester2(arg1); - 1 + 1 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/test.partial_eq.toml new file mode 100644 index 00000000000..809e4f8e062 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/insert_element_reg_reuse/test.partial_eq.toml @@ -0,0 +1,5 @@ +category = "run" +expected_result = { action = "return_data", value = "0000000000000001" } +validate_abi = true + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..668a88f2e07 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "type": "bool" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/src/main.sw index 87f47a3cef8..ad55ab7b220 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/src/main.sw @@ -6,11 +6,20 @@ enum X { Y: (), } +#[cfg(experimental_partial_eq = false)] impl Eq for X { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for X { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for X {} impl Ord for X { fn lt(self, other: Self) -> bool { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/test.partial_eq.toml new file mode 100644 index 00000000000..f0a857e8269 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "01" } +validate_abi = true +expected_warnings = 3 + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..05b0f04b2d3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/src/main.sw index 240a54ba15c..d4b3ba0b1db 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/src/main.sw @@ -8,6 +8,7 @@ enum Initialized { False: (), } +#[cfg(experimental_partial_eq = false)] impl Eq for Initialized { fn eq(self, other: Self) -> bool { match (self, other) { @@ -17,6 +18,18 @@ impl Eq for Initialized { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Initialized { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Initialized::True, Initialized::True) => true, + (Initialized::False, Initialized::False) => true, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Initialized {} impl Initialized { fn foo(self) -> bool { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/test.partial_eq.toml new file mode 100644 index 00000000000..97de0739161 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "0000000000000001" } +validate_abi = true +expected_warnings = 1 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..05b0f04b2d3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/src/main.sw index bdfe1333507..de1b29e34fd 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/src/main.sw @@ -4,42 +4,63 @@ pub trait New { fn new() -> Self; } -impl New for [u64;0] { +impl New for [u64; 0] { fn new() -> Self { [] } } -impl Eq for [u64;0] { - fn eq(self, other: Self) -> bool { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { true - } + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} -struct EmptyStruct { } +struct EmptyStruct {} impl New for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } } +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { - fn eq(self, other: Self) -> bool { + fn eq(self, other: Self) -> bool { true - } + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } } +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} #[inline(always)] fn reference_zero_sized_local_var_and_value() - where T: New + Eq +where + T: New + Eq, { assert(__size_of::() == 0); let mut x1 = T::new(); let x2 = T::new(); - let x_ptr1 = asm(r: &x1) { r: raw_ptr }; + let x_ptr1 = asm(r: &x1) { + r: raw_ptr + }; let res1 = x_ptr1.read::(); let x_ptr2 = __addr_of(x2); @@ -49,16 +70,17 @@ fn reference_zero_sized_local_var_and_value() #[inline(never)] fn reference_zero_sized_local_var_and_value_not_inlined() - where T: New + Eq +where + T: New + Eq, { reference_zero_sized_local_var_and_value::() } fn main() -> u64 { reference_zero_sized_local_var_and_value_not_inlined::(); - reference_zero_sized_local_var_and_value_not_inlined::<[u64;0]>(); + reference_zero_sized_local_var_and_value_not_inlined::<[u64; 0]>(); reference_zero_sized_local_var_and_value::(); - reference_zero_sized_local_var_and_value::<[u64;0]>(); + reference_zero_sized_local_var_and_value::<[u64; 0]>(); 42 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/test.partial_eq.toml.todo new file mode 100644 index 00000000000..d781a1cfd78 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/mut_ref_empty_type/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = true +expected_warnings = 2 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/impls.sw index 17482f14082..45be44e578e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/impls.sw @@ -70,6 +70,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -83,10 +84,30 @@ impl Eq for str[6] { i = i + 1; }; - + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -97,13 +118,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -116,11 +146,20 @@ pub struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -131,20 +170,29 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } fn different() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -152,6 +200,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -159,6 +208,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -169,11 +228,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -195,35 +263,52 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; - null_ptr.add::(42*2) + null_ptr.add::(42 * 2) } } impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - - std::raw_slice::from_parts::(null_ptr, 42*2) + let null_ptr = asm() { + zero: raw_ptr + }; + + std::raw_slice::from_parts::(null_ptr, 42 * 2) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -234,13 +319,22 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } @@ -249,8 +343,17 @@ impl TestInstance for [u64;0] { } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/main.sw index 509be0e8257..9a1b8e04e3e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/src/main.sw @@ -5,66 +5,128 @@ use impls::*; use core::ops::Eq; struct S - where T: TestInstance + Eq +where + T: TestInstance + Eq, { x: T, - y: T + y: T, } impl TestInstance for S - where T: TestInstance + Eq +where + T: TestInstance + Eq, { fn new() -> Self { - S { x: T::new(), y: T::new() } + S { + x: T::new(), + y: T::new(), + } } fn different() -> Self { - S { x: T::different(), y: T::different() } + S { + x: T::different(), + y: T::different(), + } } } +#[cfg(experimental_partial_eq = false)] impl Eq for S - where T: TestInstance + Eq +where + T: TestInstance + Eq, { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for S +where + T: TestInstance + PartialEq, +{ + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for S +where + T: TestInstance + Eq, +{} struct EmbedsReferences - where T: TestInstance + Eq +where + T: TestInstance + Eq, { x: & & &T, - y: & &T + y: & &T, } +#[cfg(experimental_partial_eq = false)] impl Eq for & &T - where T: TestInstance + Eq +where + T: TestInstance + Eq, { fn eq(self, other: Self) -> bool { **self == **other } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for & &T +where + T: TestInstance + PartialEq, +{ + fn eq(self, other: Self) -> bool { + **self == **other + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for & &T +where + T: TestInstance + Eq, +{} +#[cfg(experimental_partial_eq = false)] impl Eq for & & &T - where T: TestInstance + Eq +where + T: TestInstance + Eq, { fn eq(self, other: Self) -> bool { ***self == ***other } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for & & &T +where + T: TestInstance + PartialEq, +{ + fn eq(self, other: Self) -> bool { + ***self == ***other + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for & & &T +where + T: TestInstance + Eq, +{} struct EmbedsReferencesMut - where T: TestInstance + Eq +where + T: TestInstance + Eq, { x: &mut &mut &mut T, - y: &mut &mut T + y: &mut &mut T, } #[inline(always)] fn dereference_struct() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { - let mut s = S { x: T::new(), y: T::different() }; + let mut s = S { + x: T::new(), + y: T::different(), + }; let r_s = &s; let r_r_s = &r_s; @@ -128,20 +190,34 @@ fn dereference_struct() #[inline(never)] fn dereference_struct_not_inlined() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { dereference_struct::() } #[inline(always)] fn dereference_struct_of_refs() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { - let mut s1 = S { x: T::new(), y: T::different() }; - let mut s2 = S { x: T::new(), y: T::different() }; - - let embed = EmbedsReferences { x: & & &s1, y: & &s2 }; - let mut embed_mut = EmbedsReferencesMut { x: &mut &mut &mut s1, y: &mut &mut s2 }; + let mut s1 = S { + x: T::new(), + y: T::different(), + }; + let mut s2 = S { + x: T::new(), + y: T::different(), + }; + + let embed = EmbedsReferences { + x: & & &s1, + y: & &s2, + }; + let mut embed_mut = EmbedsReferencesMut { + x: &mut &mut &mut s1, + y: &mut &mut s2, + }; let r_embed = &embed; let r_r_embed = &r_embed; @@ -217,12 +293,18 @@ fn dereference_struct_of_refs() assert(r_r_r_embed.y.x == r_mut_r_mut_r_mut_embed_mut.y.x); assert(r_r_r_embed.y.y == r_mut_r_mut_r_mut_embed_mut.y.y); - let r = & & & & &EmbedsReferences { x: & & &T::new(), y: & &T::different() }; + let r = & & & & &EmbedsReferences { + x: & & &T::new(), + y: & &T::different(), + }; assert(r.x == & & &T::new()); assert(r.y == & &T::different()); - let r = & & & & &EmbedsReferencesMut { x: &mut &mut &mut T::new(), y: &mut &mut T::different() }; + let r = & & & & &EmbedsReferencesMut { + x: &mut &mut &mut T::new(), + y: &mut &mut T::different(), + }; assert(r.x == &mut &mut &mut T::new()); assert(r.y == &mut &mut T::different()); @@ -230,7 +312,8 @@ fn dereference_struct_of_refs() #[inline(never)] fn dereference_struct_of_refs_not_inlined() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { dereference_struct_of_refs::() } @@ -244,8 +327,8 @@ fn test_all_inlined() { dereference_struct::(); dereference_struct::(); dereference_struct::(); - dereference_struct::<[u64;2]>(); - dereference_struct::<[u64;0]>(); + dereference_struct::<[u64; 2]>(); + dereference_struct::<[u64; 0]>(); dereference_struct::(); dereference_struct::(); dereference_struct::(); @@ -263,8 +346,8 @@ fn test_all_inlined() { dereference_struct_of_refs::(); dereference_struct_of_refs::(); dereference_struct_of_refs::(); - dereference_struct_of_refs::<[u64;2]>(); - dereference_struct_of_refs::<[u64;0]>(); + dereference_struct_of_refs::<[u64; 2]>(); + dereference_struct_of_refs::<[u64; 0]>(); dereference_struct_of_refs::(); dereference_struct_of_refs::(); dereference_struct_of_refs::(); @@ -285,8 +368,8 @@ fn test_not_inlined() { dereference_struct_not_inlined::(); dereference_struct_not_inlined::(); dereference_struct_not_inlined::(); - dereference_struct_not_inlined::<[u64;2]>(); - dereference_struct_not_inlined::<[u64;0]>(); + dereference_struct_not_inlined::<[u64; 2]>(); + dereference_struct_not_inlined::<[u64; 0]>(); dereference_struct_not_inlined::(); dereference_struct_not_inlined::(); dereference_struct_not_inlined::(); @@ -304,8 +387,8 @@ fn test_not_inlined() { dereference_struct_of_refs_not_inlined::(); dereference_struct_of_refs_not_inlined::(); dereference_struct_of_refs_not_inlined::(); - dereference_struct_of_refs_not_inlined::<[u64;2]>(); - dereference_struct_of_refs_not_inlined::<[u64;0]>(); + dereference_struct_of_refs_not_inlined::<[u64; 2]>(); + dereference_struct_of_refs_not_inlined::<[u64; 0]>(); dereference_struct_of_refs_not_inlined::(); dereference_struct_of_refs_not_inlined::(); dereference_struct_of_refs_not_inlined::(); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/test.partial_eq.toml.todo new file mode 100644 index 00000000000..c784e5b3002 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_structs/test.partial_eq.toml.todo @@ -0,0 +1,15 @@ +# TODO: Enable this `partial_eq` test once the compiler stack overflow is fixed. +# The migrated code, left as is, causes compiler stack overflow. +# The code in `main.sw` uses a weird combination of generic types +# having trait constraints on `Eq` and implementing `PartialEq`s +# on those types that have `PartialEq` trait constraints on same +# generic arguments. +# The test could very likely be fixed by types having `PartialEq` +# constraints instead of `Eq` (which also semantically makes sense) +# but it is worth first exploring and fixing the compiler stack overflow. +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 2 # TODO-DCA: Set to zero once https://github.com/FuelLabs/sway/issues/5921 is fixed. + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/impls.sw index 17482f14082..45be44e578e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/impls.sw @@ -70,6 +70,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -83,10 +84,30 @@ impl Eq for str[6] { i = i + 1; }; - + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -97,13 +118,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -116,11 +146,20 @@ pub struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -131,20 +170,29 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } fn different() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -152,6 +200,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -159,6 +208,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -169,11 +228,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -195,35 +263,52 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; - null_ptr.add::(42*2) + null_ptr.add::(42 * 2) } } impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - - std::raw_slice::from_parts::(null_ptr, 42*2) + let null_ptr = asm() { + zero: raw_ptr + }; + + std::raw_slice::from_parts::(null_ptr, 42 * 2) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -234,13 +319,22 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } @@ -249,8 +343,17 @@ impl TestInstance for [u64;0] { } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/main.sw index 8a0add28f23..ff558f17075 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/src/main.sw @@ -4,25 +4,58 @@ mod impls; use impls::*; use core::ops::Eq; +#[cfg(experimental_partial_eq = false)] impl Eq for & &T - where T: TestInstance + Eq +where + T: TestInstance + Eq, { fn eq(self, other: Self) -> bool { **self == **other } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for & &T +where + T: TestInstance + PartialEq, +{ + fn eq(self, other: Self) -> bool { + **self == **other + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for & &T +where + T: TestInstance + Eq, +{} +#[cfg(experimental_partial_eq = false)] impl Eq for & & &T - where T: TestInstance + Eq +where + T: TestInstance + Eq, +{ + fn eq(self, other: Self) -> bool { + ***self == ***other + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for & & &T +where + T: TestInstance + PartialEq, { fn eq(self, other: Self) -> bool { ***self == ***other } } +#[cfg(experimental_partial_eq = true)] +impl Eq for & & &T +where + T: TestInstance + Eq, +{} #[inline(always)] fn dereference_tuple() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { let mut x = (T::new(), T::different()); @@ -88,14 +121,16 @@ fn dereference_tuple() #[inline(never)] fn dereference_tuple_not_inlined() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { dereference_tuple::() } #[inline(always)] fn dereference_tuple_of_refs() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { let mut x1 = (T::new(), T::different()); let mut x2 = (T::new(), T::different()); @@ -190,7 +225,8 @@ fn dereference_tuple_of_refs() #[inline(never)] fn dereference_tuple_of_refs_not_inlined() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { dereference_tuple_of_refs::() } @@ -204,8 +240,8 @@ fn test_all_inlined() { dereference_tuple::(); dereference_tuple::(); dereference_tuple::(); - dereference_tuple::<[u64;2]>(); - dereference_tuple::<[u64;0]>(); + dereference_tuple::<[u64; 2]>(); + dereference_tuple::<[u64; 0]>(); dereference_tuple::(); dereference_tuple::(); dereference_tuple::(); @@ -215,7 +251,7 @@ fn test_all_inlined() { dereference_tuple::(); dereference_tuple::(); dereference_tuple::(); - + dereference_tuple_of_refs::<()>(); dereference_tuple_of_refs::(); dereference_tuple_of_refs::(); @@ -223,8 +259,8 @@ fn test_all_inlined() { dereference_tuple_of_refs::(); dereference_tuple_of_refs::(); dereference_tuple_of_refs::(); - dereference_tuple_of_refs::<[u64;2]>(); - dereference_tuple_of_refs::<[u64;0]>(); + dereference_tuple_of_refs::<[u64; 2]>(); + dereference_tuple_of_refs::<[u64; 0]>(); dereference_tuple_of_refs::(); dereference_tuple_of_refs::(); dereference_tuple_of_refs::(); @@ -245,8 +281,8 @@ fn test_not_inlined() { dereference_tuple_not_inlined::(); dereference_tuple_not_inlined::(); dereference_tuple_not_inlined::(); - dereference_tuple_not_inlined::<[u64;2]>(); - dereference_tuple_not_inlined::<[u64;0]>(); + dereference_tuple_not_inlined::<[u64; 2]>(); + dereference_tuple_not_inlined::<[u64; 0]>(); dereference_tuple_not_inlined::(); dereference_tuple_not_inlined::(); dereference_tuple_not_inlined::(); @@ -256,7 +292,7 @@ fn test_not_inlined() { dereference_tuple_not_inlined::(); dereference_tuple_not_inlined::(); dereference_tuple_not_inlined::(); - + dereference_tuple_of_refs_not_inlined::<()>(); dereference_tuple_of_refs_not_inlined::(); dereference_tuple_of_refs_not_inlined::(); @@ -264,8 +300,8 @@ fn test_not_inlined() { dereference_tuple_of_refs_not_inlined::(); dereference_tuple_of_refs_not_inlined::(); dereference_tuple_of_refs_not_inlined::(); - dereference_tuple_of_refs_not_inlined::<[u64;2]>(); - dereference_tuple_of_refs_not_inlined::<[u64;0]>(); + dereference_tuple_of_refs_not_inlined::<[u64; 2]>(); + dereference_tuple_of_refs_not_inlined::<[u64; 0]>(); dereference_tuple_of_refs_not_inlined::(); dereference_tuple_of_refs_not_inlined::(); dereference_tuple_of_refs_not_inlined::(); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/test.partial_eq.toml.todo new file mode 100644 index 00000000000..300d50c2e1b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_dot_on_tuples/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/src/impls.sw index 17482f14082..45be44e578e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/src/impls.sw @@ -70,6 +70,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -83,10 +84,30 @@ impl Eq for str[6] { i = i + 1; }; - + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -97,13 +118,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -116,11 +146,20 @@ pub struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -131,20 +170,29 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } fn different() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -152,6 +200,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -159,6 +208,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -169,11 +228,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -195,35 +263,52 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; - null_ptr.add::(42*2) + null_ptr.add::(42 * 2) } } impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - - std::raw_slice::from_parts::(null_ptr, 42*2) + let null_ptr = asm() { + zero: raw_ptr + }; + + std::raw_slice::from_parts::(null_ptr, 42 * 2) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -234,13 +319,22 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } @@ -249,8 +343,17 @@ impl TestInstance for [u64;0] { } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/test.partial_eq.toml.todo new file mode 100644 index 00000000000..7792c2dd7a6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_index/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 50 # TODO-DCA: Set to zero once https://github.com/FuelLabs/sway/issues/5921 is fixed. + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/src/impls.sw index 17482f14082..45be44e578e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/src/impls.sw @@ -70,6 +70,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -83,10 +84,30 @@ impl Eq for str[6] { i = i + 1; }; - + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -97,13 +118,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -116,11 +146,20 @@ pub struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -131,20 +170,29 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } fn different() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -152,6 +200,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -159,6 +208,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -169,11 +228,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -195,35 +263,52 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; - null_ptr.add::(42*2) + null_ptr.add::(42 * 2) } } impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - - std::raw_slice::from_parts::(null_ptr, 42*2) + let null_ptr = asm() { + zero: raw_ptr + }; + + std::raw_slice::from_parts::(null_ptr, 42 * 2) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -234,13 +319,22 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } @@ -249,8 +343,17 @@ impl TestInstance for [u64;0] { } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/test.partial_eq.toml.todo new file mode 100644 index 00000000000..bd94d0f0824 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/dereferencing_operator_star/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/src/impls.sw index 46564dda772..8c6d31eeaad 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/src/impls.sw @@ -48,6 +48,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -61,10 +62,30 @@ impl Eq for str[6] { i = i + 1; }; - + true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -72,13 +93,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -88,11 +118,20 @@ pub struct Struct { pub x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -100,17 +139,26 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -118,6 +166,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -125,6 +174,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -132,11 +191,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -152,7 +220,9 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } @@ -160,17 +230,28 @@ impl TestInstance for raw_ptr { impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -178,20 +259,38 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/test.partial_eq.toml.todo new file mode 100644 index 00000000000..dfbe60f885e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/passing_and_returning_references_to_and_from_functions/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return", value = 42 } +expected_result_new_encoding = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/src/main.sw index c8139b1560b..b95b4d15795 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/src/main.sw @@ -2,14 +2,17 @@ script; struct A { r_u8: &mut u8, - r_array: &mut [u64;3], + r_array: &mut [u64; 3], } impl A { fn new() -> Self { - Self { r_u8: &mut 0, r_array: &mut [0, 0, 0] } + Self { + r_u8: &mut 0, + r_array: &mut [0, 0, 0], + } } - + fn use_me(self) { poke(self.r_u8); poke(self.r_array); @@ -18,34 +21,52 @@ impl A { struct B { r_a: &A, - r_array: &[&A;3], + r_array: &[&A; 3], } impl B { fn new() -> Self { let r_a = &A::new(); - Self { r_a: r_a, r_array: &[r_a, r_a, r_a] } + Self { + r_a: r_a, + r_array: &[r_a, r_a, r_a], + } } - + fn use_me(self) { poke(self.r_a); poke(self.r_array); } } -impl core::ops::Eq for [u64;3] { +#[cfg(experimental_partial_eq = false)] +impl core::ops::Eq for [u64; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for [u64; 3] { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] && self[2] == other[2] } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for [u64; 3] {} #[inline(always)] fn in_structs() { let mut x = 11u8; - let mut array: [u64;3] = [11, 11, 11]; + let mut array: [u64; 3] = [11, 11, 11]; - let a = A { r_u8: &mut x, r_array: &mut array }; - let b = B { r_a: &a, r_array: &[&a, &a, &a] }; + let a = A { + r_u8: &mut x, + r_array: &mut array, + }; + let b = B { + r_a: &a, + r_array: &[&a, &a, &a], + }; *a.r_u8 = 22; assert_eq(x, 22); @@ -76,10 +97,16 @@ enum E { #[inline(always)] fn in_enums() { let mut x = 11u8; - let mut array: [u64;3] = [11, 11, 11]; + let mut array: [u64; 3] = [11, 11, 11]; - let a = A { r_u8: &mut x, r_array: &mut array }; - let b = B { r_a: &a, r_array: &[&a, &a, &a] }; + let a = A { + r_u8: &mut x, + r_array: &mut array, + }; + let b = B { + r_a: &a, + r_array: &[&a, &a, &a], + }; let e_r_a = E::R_A(&a); let e_r_b = E::R_B(&b); @@ -118,10 +145,16 @@ fn in_enums_not_inlined() { #[inline(always)] fn in_arrays() { let mut x = 11u8; - let mut array: [u64;3] = [11, 11, 11]; + let mut array: [u64; 3] = [11, 11, 11]; - let a = A { r_u8: &mut x, r_array: &mut array }; - let b = B { r_a: &a, r_array: &[&a, &a, &a] }; + let a = A { + r_u8: &mut x, + r_array: &mut array, + }; + let b = B { + r_a: &a, + r_array: &[&a, &a, &a], + }; let arr_a = [&a, &a, &a]; let arr_b = [&b, &b, &b]; @@ -150,10 +183,16 @@ fn in_arrays_not_inlined() { #[inline(always)] fn in_tuples() { let mut x = 11u8; - let mut array: [u64;3] = [11, 11, 11]; + let mut array: [u64; 3] = [11, 11, 11]; - let a = A { r_u8: &mut x, r_array: &mut array }; - let b = B { r_a: &a, r_array: &[&a, &a, &a] }; + let a = A { + r_u8: &mut x, + r_array: &mut array, + }; + let b = B { + r_a: &a, + r_array: &[&a, &a, &a], + }; let tuple_a = (&a, &a, &a); let tuple_b = (&b, &b, &b); @@ -205,4 +244,4 @@ fn main() -> u64 { 42 } -fn poke(_x: T) { } \ No newline at end of file +fn poke(_x: T) {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/test.partial_eq.toml new file mode 100644 index 00000000000..5989d047eae --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_in_aggregates/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 2 # TODO-DCA: Set to zero once https://github.com/FuelLabs/sway/issues/5921 is fixed. + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/src/impls.sw index 0d80e865299..510697a7d54 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/src/impls.sw @@ -70,6 +70,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -83,10 +84,30 @@ impl Eq for str[6] { i = i + 1; }; - + true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -97,13 +118,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -116,11 +146,20 @@ pub struct Struct { pub x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -131,20 +170,29 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } fn different() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -152,6 +200,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -159,6 +208,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -169,11 +228,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -195,42 +263,61 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; - null_ptr.add::(42*2) + null_ptr.add::(42 * 2) } } impl AbiEncode for raw_ptr { fn abi_encode(self, buffer: Buffer) -> Buffer { - let v = asm(p: self) { p: u64 }; + let v = asm(p: self) { + p: u64 + }; v.abi_encode(buffer) } } impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - - std::raw_slice::from_parts::(null_ptr, 42*2) + let null_ptr = asm() { + zero: raw_ptr + }; + + std::raw_slice::from_parts::(null_ptr, 42 * 2) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -241,13 +328,22 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } @@ -256,8 +352,17 @@ impl TestInstance for [u64;0] { } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/test.partial_eq.toml.todo new file mode 100644 index 00000000000..a40cf115534 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_passed_and_returned_to_and_from_functions/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return", value = 42 } +expected_result_new_encoding = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 11 # TODO-DCA: Set to zero once https://github.com/FuelLabs/sway/issues/5921 is fixed. + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/src/impls.sw index bf6a011f69c..cf0a00ea754 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/src/impls.sw @@ -72,6 +72,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -85,10 +86,30 @@ impl Eq for str[6] { i = i + 1; }; - + true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -99,13 +120,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -118,11 +148,20 @@ pub struct Struct { pub x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -133,20 +172,29 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } fn different() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -154,6 +202,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -161,6 +210,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -171,11 +230,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -197,42 +265,61 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; - null_ptr.add::(42*2) + null_ptr.add::(42 * 2) } } impl AbiEncode for raw_ptr { fn abi_encode(self, buffer: Buffer) -> Buffer { - let v = asm(p: self) { p: u64 }; + let v = asm(p: self) { + p: u64 + }; v.abi_encode(buffer) } } impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - - std::raw_slice::from_parts::(null_ptr, 42*2) + let null_ptr = asm() { + zero: raw_ptr + }; + + std::raw_slice::from_parts::(null_ptr, 42 * 2) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -243,13 +330,22 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } @@ -258,8 +354,17 @@ impl TestInstance for [u64;0] { } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/test.partial_eq.toml.todo new file mode 100644 index 00000000000..c7f5fd2bf7b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_expressions/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return", value = 42 } +expected_result_new_encoding = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 1 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/impls.sw index dad85dd3649..8a4e09b827e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/impls.sw @@ -72,6 +72,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -85,10 +86,30 @@ impl Eq for str[6] { i = i + 1; }; - + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn new() -> Self { @@ -99,13 +120,22 @@ impl TestInstance for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl TestInstance for [u64;2] { +impl TestInstance for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -118,11 +148,20 @@ pub struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn new() -> Self { @@ -133,20 +172,29 @@ impl TestInstance for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } fn different() -> Self { - EmptyStruct { } + EmptyStruct {} } } @@ -154,6 +202,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -161,6 +210,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn new() -> Self { @@ -171,11 +230,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn new() -> Self { @@ -197,35 +265,52 @@ impl TestInstance for b256 { impl TestInstance for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; - null_ptr.add::(42*2) + null_ptr.add::(42 * 2) } } impl TestInstance for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } fn different() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - - std::raw_slice::from_parts::(null_ptr, 42*2) + let null_ptr = asm() { + zero: raw_ptr + }; + + std::raw_slice::from_parts::(null_ptr, 42 * 2) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn new() -> Self { @@ -236,13 +321,22 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl TestInstance for [u64;0] { +impl TestInstance for [u64; 0] { fn new() -> Self { [] } @@ -251,8 +345,17 @@ impl TestInstance for [u64;0] { } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/main.sw index c5cf9849fd9..a1bd5ff905c 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/src/main.sw @@ -36,32 +36,68 @@ struct S { y: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for S { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for S { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for S {} #[inline(always)] fn assign_struct_value() { - let mut x = S { x: true, y: 111 }; + let mut x = S { + x: true, + y: 111, + }; let mut r_mut_x = &mut x; let mut r_mut_r_mut_x = &mut r_mut_x; let r_mut_r_mut_r_mut_x = &mut r_mut_r_mut_x; - *r_mut_x = S { x: false, y: 222 }; - assert(x == S { x: false, y: 222 }); - - **r_mut_r_mut_x = S { x: true, y: 333 }; - assert(x == S { x: true, y: 333 }); - - ***r_mut_r_mut_r_mut_x = S { x: false, y: 444 }; - assert(x == S { x: false, y: 444 }); + *r_mut_x = S { + x: false, + y: 222, + }; + assert(x == S { + x: false, + y: 222, + }); + + **r_mut_r_mut_x = S { + x: true, + y: 333, + }; + assert(x == S { + x: true, + y: 333, + }); + + ***r_mut_r_mut_r_mut_x = S { + x: false, + y: 444, + }; + assert(x == S { + x: false, + y: 444, + }); let r = & &mut & &mut x; - ****r = S { x: true, y: 111 }; - assert(x == S { x: true, y: 111 }); + ****r = S { + x: true, + y: 111, + }; + assert(x == S { + x: true, + y: 111, + }); } #[inline(never)] @@ -69,11 +105,20 @@ fn assign_struct_value_not_inlined() { assign_struct_value() } +#[cfg(experimental_partial_eq = false)] impl Eq for (bool, u64) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (bool, u64) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (bool, u64) {} #[inline(always)] fn assign_tuple_value() { @@ -109,6 +154,7 @@ enum E { D: u32, } +#[cfg(experimental_partial_eq = false)] impl Eq for E { fn eq(self, other: Self) -> bool { match (self, other) { @@ -120,6 +166,20 @@ impl Eq for E { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for E { + fn eq(self, other: Self) -> bool { + match (self, other) { + (E::A(l), E::A(r)) => l == r, + (E::B(l), E::B(r)) => l == r, + (E::C(l), E::C(r)) => l == r, + (E::D(l), E::D(r)) => l == r, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for E {} #[inline(always)] fn assign_enum_value() { @@ -177,7 +237,8 @@ fn assign_array_value_not_inlined() { #[inline(always)] fn assign_value() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { let mut x = T::new(); @@ -201,7 +262,8 @@ fn assign_value() #[inline(never)] fn assign_value_not_inlined() - where T: TestInstance + Eq +where + T: TestInstance + Eq, { assign_value::() } @@ -222,8 +284,8 @@ fn test_all_inlined() { assign_value::(); // TODO: Enable once https://github.com/FuelLabs/sway/issues/5833 get solved. // assign_value::(); - assign_value::<[u64;2]>(); - assign_value::<[u64;0]>(); + assign_value::<[u64; 2]>(); + assign_value::<[u64; 0]>(); assign_value::(); assign_value::(); assign_value::(); @@ -252,8 +314,8 @@ fn test_not_inlined() { assign_value_not_inlined::(); // TODO: Enable once https://github.com/FuelLabs/sway/issues/5833 get solved. // assign_value_not_inlined::(); - assign_value_not_inlined::<[u64;2]>(); - assign_value_not_inlined::<[u64;0]>(); + assign_value_not_inlined::<[u64; 2]>(); + assign_value_not_inlined::<[u64; 0]>(); assign_value_not_inlined::(); assign_value_not_inlined::(); assign_value_not_inlined::(); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/test.partial_eq.toml.todo new file mode 100644 index 00000000000..dfbe60f885e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/reassigning_via_references_to_values/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return", value = 42 } +expected_result_new_encoding = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/src/main.sw index 2d817fcbb51..0c70de4fdf2 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/src/main.sw @@ -6,13 +6,24 @@ struct A { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for A { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for A { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for A {} -struct S where T: Eq +struct S +where + T: Eq, { r_t: &T, r_r_t: & &T, @@ -20,19 +31,39 @@ struct S where T: Eq r_mut_r_mut_t: &mut &mut T, } -impl Eq for S where T: Eq { +#[cfg(experimental_partial_eq = false)] +impl Eq for S +where + T: Eq, +{ fn eq(self, other: Self) -> bool { // Original implementation, before we had dereferencing. // Let's keep it for more paranoid coverage :-) - let self_r_t_ptr = asm(r: self.r_t) { r: raw_ptr }; - let self_r_r_t_ptr = asm(r: self.r_r_t) { r: raw_ptr }; - let self_r_mut_t_ptr = asm(r: self.r_mut_t) { r: raw_ptr }; - let self_r_mut_r_mut_t_ptr = asm(r: self.r_mut_r_mut_t) { r: raw_ptr }; + let self_r_t_ptr = asm(r: self.r_t) { + r: raw_ptr + }; + let self_r_r_t_ptr = asm(r: self.r_r_t) { + r: raw_ptr + }; + let self_r_mut_t_ptr = asm(r: self.r_mut_t) { + r: raw_ptr + }; + let self_r_mut_r_mut_t_ptr = asm(r: self.r_mut_r_mut_t) { + r: raw_ptr + }; - let other_r_t_ptr = asm(r: other.r_t) { r: raw_ptr }; - let other_r_r_t_ptr = asm(r: other.r_r_t) { r: raw_ptr }; - let other_r_mut_t_ptr = asm(r: other.r_mut_t) { r: raw_ptr }; - let other_r_mut_r_mut_t_ptr = asm(r: other.r_mut_r_mut_t) { r: raw_ptr }; + let other_r_t_ptr = asm(r: other.r_t) { + r: raw_ptr + }; + let other_r_r_t_ptr = asm(r: other.r_r_t) { + r: raw_ptr + }; + let other_r_mut_t_ptr = asm(r: other.r_mut_t) { + r: raw_ptr + }; + let other_r_mut_r_mut_t_ptr = asm(r: other.r_mut_r_mut_t) { + r: raw_ptr + }; // And use the opportunity to assert dereferencing ;-) assert(self_r_t_ptr.read::() == *self.r_t); @@ -44,21 +75,105 @@ impl Eq for S where T: Eq { assert(self_r_mut_r_mut_t_ptr.read::().read::() == **self.r_mut_r_mut_t); assert(other_r_mut_r_mut_t_ptr.read::().read::() == **other.r_mut_r_mut_t); - self_r_t_ptr.read::() == other_r_t_ptr.read::() - && - self_r_r_t_ptr.read::().read::() == other_r_r_t_ptr.read::().read::() - && - self_r_mut_t_ptr.read::() == other_r_mut_t_ptr.read::() - && - self_r_mut_r_mut_t_ptr.read::().read::() == other_r_mut_r_mut_t_ptr.read::().read::() + self_r_t_ptr + .read::() == other_r_t_ptr + .read::() + && self_r_r_t_ptr + .read::() + .read::() == other_r_r_t_ptr + .read::() + .read::() + && self_r_mut_t_ptr + .read::() == other_r_mut_t_ptr + .read::() + && self_r_mut_r_mut_t_ptr + .read::() + .read::() == other_r_mut_r_mut_t_ptr + .read::() + .read::() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for S +where + T: PartialEq, +{ + fn eq(self, other: Self) -> bool { + let self_r_t_ptr = asm(r: self.r_t) { + r: raw_ptr + }; + let self_r_r_t_ptr = asm(r: self.r_r_t) { + r: raw_ptr + }; + let self_r_mut_t_ptr = asm(r: self.r_mut_t) { + r: raw_ptr + }; + let self_r_mut_r_mut_t_ptr = asm(r: self.r_mut_r_mut_t) { + r: raw_ptr + }; -fn test(s: S, v: T) where T: Eq { - let s_r_t_ptr = asm(r: s.r_t) { r: raw_ptr }; - let s_r_r_t_ptr = asm(r: s.r_r_t) { r: raw_ptr }; - let s_r_mut_t_ptr = asm(r: s.r_mut_t) { r: raw_ptr }; - let s_r_mut_r_mut_t_ptr = asm(r: s.r_mut_r_mut_t) { r: raw_ptr }; + let other_r_t_ptr = asm(r: other.r_t) { + r: raw_ptr + }; + let other_r_r_t_ptr = asm(r: other.r_r_t) { + r: raw_ptr + }; + let other_r_mut_t_ptr = asm(r: other.r_mut_t) { + r: raw_ptr + }; + let other_r_mut_r_mut_t_ptr = asm(r: other.r_mut_r_mut_t) { + r: raw_ptr + }; + + assert(self_r_t_ptr.read::() == *self.r_t); + assert(other_r_t_ptr.read::() == *other.r_t); + assert(self_r_r_t_ptr.read::().read::() == **self.r_r_t); + assert(other_r_r_t_ptr.read::().read::() == **other.r_r_t); + assert(self_r_mut_t_ptr.read::() == *self.r_mut_t); + assert(other_r_mut_t_ptr.read::() == *other.r_mut_t); + assert(self_r_mut_r_mut_t_ptr.read::().read::() == **self.r_mut_r_mut_t); + assert(other_r_mut_r_mut_t_ptr.read::().read::() == **other.r_mut_r_mut_t); + + self_r_t_ptr + .read::() == other_r_t_ptr + .read::() + && self_r_r_t_ptr + .read::() + .read::() == other_r_r_t_ptr + .read::() + .read::() + && self_r_mut_t_ptr + .read::() == other_r_mut_t_ptr + .read::() + && self_r_mut_r_mut_t_ptr + .read::() + .read::() == other_r_mut_r_mut_t_ptr + .read::() + .read::() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for S +where + T: Eq, +{} + +fn test(s: S, v: T) +where + T: Eq, +{ + let s_r_t_ptr = asm(r: s.r_t) { + r: raw_ptr + }; + let s_r_r_t_ptr = asm(r: s.r_r_t) { + r: raw_ptr + }; + let s_r_mut_t_ptr = asm(r: s.r_mut_t) { + r: raw_ptr + }; + let s_r_mut_r_mut_t_ptr = asm(r: s.r_mut_r_mut_t) { + r: raw_ptr + }; assert(s_r_t_ptr.read::() == v); assert(s_r_r_t_ptr.read::().read::() == v); @@ -73,17 +188,32 @@ fn test(s: S, v: T) where T: Eq { fn main() -> u64 { let mut x = 123u8; - - let mut s_x = S { r_t: &x, r_r_t: & &x, r_mut_t: &mut x, r_mut_r_mut_t: &mut &mut x }; + + let mut s_x = S { + r_t: &x, + r_r_t: & &x, + r_mut_t: &mut x, + r_mut_r_mut_t: &mut &mut x, + }; test(s_x, x); - - let s_s_x = S { r_t: &s_x, r_r_t: & &s_x, r_mut_t: &mut s_x, r_mut_r_mut_t: &mut &mut s_x }; + + let s_s_x = S { + r_t: &s_x, + r_r_t: & &s_x, + r_mut_t: &mut s_x, + r_mut_r_mut_t: &mut &mut s_x, + }; test(s_s_x, s_x); let mut a = A { x: 321 }; - - let s_a = S { r_t: &a, r_r_t: & &a, r_mut_t: &mut a, r_mut_r_mut_t: &mut &mut a }; + + let s_a = S { + r_t: &a, + r_r_t: & &a, + r_mut_t: &mut a, + r_mut_r_mut_t: &mut &mut a, + }; test(s_a, a); - + 42 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/test.partial_eq.toml.todo new file mode 100644 index 00000000000..300d50c2e1b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/references_and_generics/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/src/main.sw index ce1a42d8c09..5633eb53dac 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/src/main.sw @@ -6,43 +6,52 @@ struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} +#[cfg(experimental_partial_eq = false)] impl Eq for [Struct; 3] { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] && self[2] == other[2] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [Struct; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [Struct; 3] {} // TODO-IG: Add tests for other expressions. #[inline(always)] -fn if_expr(input: u64, left: T, right: T) where T: AbiEncode + Eq { - let mut x = if input > 42 { - left - } else { - right - }; +fn if_expr(input: u64, left: T, right: T) +where + T: AbiEncode + Eq, +{ + let mut x = if input > 42 { left } else { right }; let r_x = &x; - let r_val = &if input > 42 { - left - } else { - right - }; + let r_val = &if input > 42 { left } else { right }; let r_mut_x = &mut x; - let r_mut_val = &mut if input > 42 { - left - } else { - right - }; + let r_mut_val = &mut if input > 42 { left } else { right }; assert_references(r_x, r_val, r_mut_x, r_mut_val, x); @@ -69,11 +78,28 @@ fn if_expr(input: u64, left: T, right: T) where T: AbiEncode + Eq { } } -fn assert_references(r_x: &T, r_val: &T, r_mut_x: &mut T, r_mut_val: &mut T, x: T) where T: Eq { - let r_x_ptr = asm(r: r_x) { r: raw_ptr }; - let r_mut_x_ptr = asm(r: r_mut_x) { r: raw_ptr }; - let r_val_ptr = asm(r: r_val) { r: raw_ptr }; - let r_mut_val_ptr = asm(r: r_mut_val) { r: raw_ptr }; +fn assert_references( + r_x: &T, + r_val: &T, + r_mut_x: &mut T, + r_mut_val: &mut T, + x: T, +) +where + T: Eq, +{ + let r_x_ptr = asm(r: r_x) { + r: raw_ptr + }; + let r_mut_x_ptr = asm(r: r_mut_x) { + r: raw_ptr + }; + let r_val_ptr = asm(r: r_val) { + r: raw_ptr + }; + let r_mut_val_ptr = asm(r: r_mut_val) { + r: raw_ptr + }; assert(r_x_ptr == r_mut_x_ptr); assert(r_val_ptr != r_mut_val_ptr); @@ -97,7 +123,10 @@ fn assert_references(r_x: &T, r_val: &T, r_mut_x: &mut T, r_mut_val: &mut T, } #[inline(never)] -fn if_expr_not_inlined(input: u64, left: T, right: T) where T: AbiEncode + Eq { +fn if_expr_not_inlined(input: u64, left: T, right: T) +where + T: AbiEncode + Eq, +{ if_expr(input, left, right) } @@ -105,14 +134,22 @@ fn if_expr_not_inlined(input: u64, left: T, right: T) where T: AbiEncode + Eq fn test_all_inlined(input: u64) { if_expr(input, 123, 321); if_expr(input, Struct { x: 123 }, Struct { x: 321 }); - if_expr(input, [Struct { x: 123 }, Struct { x: 123 }, Struct { x: 123 }], [Struct { x: 321 }, Struct { x: 321 }, Struct { x: 321 }]); + if_expr( + input, + [Struct { x: 123 }, Struct { x: 123 }, Struct { x: 123 }], + [Struct { x: 321 }, Struct { x: 321 }, Struct { x: 321 }], + ); } #[inline(never)] fn test_not_inlined(input: u64) { if_expr_not_inlined(input, 123, 321); if_expr_not_inlined(input, Struct { x: 123 }, Struct { x: 321 }); - if_expr_not_inlined(input, [Struct { x: 123 }, Struct { x: 123 }, Struct { x: 123 }], [Struct { x: 321 }, Struct { x: 321 }, Struct { x: 321 }]); + if_expr_not_inlined( + input, + [Struct { x: 123 }, Struct { x: 123 }, Struct { x: 123 }], + [Struct { x: 321 }, Struct { x: 321 }, Struct { x: 321 }], + ); } fn main() -> u64 { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/test.partial_eq.toml.todo new file mode 100644 index 00000000000..dfbe60f885e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_expressions/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return", value = 42 } +expected_result_new_encoding = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/src/main.sw index 541489651cf..4607330f909 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/src/main.sw @@ -10,17 +10,26 @@ impl S { fn new() -> Self { Self { x: 0 } } - + fn use_me(self) { poke(self.x); } } +#[cfg(experimental_partial_eq = false)] impl Eq for S { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for S { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for S {} // TODO-IG: Extend with `mut` parameters once declaring `mut` parameters is implemented. @@ -31,7 +40,7 @@ impl Eq for S { // fn u8_parameter(p: u8) { // let r_p_1 = &p; // let r_p_2 = &p; - + // let p_ptr = asm(r: &p) { r: raw_ptr }; // let r_p_1_ptr = asm(r: r_p_1) { r: raw_ptr }; // let r_p_2_ptr = asm(r: r_p_2) { r: raw_ptr }; @@ -47,53 +56,83 @@ impl Eq for S { // u8_parameter(p) // } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} #[inline(always)] -fn array_parameter(p: [u64;2]) { +fn array_parameter(p: [u64; 2]) { let r_p_1 = &p; let r_p_2 = &p; - - let p_ptr = asm(r: &p) { r: raw_ptr }; - let r_p_1_ptr = asm(r: r_p_1) { r: raw_ptr }; - let r_p_2_ptr = asm(r: r_p_2) { r: raw_ptr }; + + let p_ptr = asm(r: &p) { + r: raw_ptr + }; + let r_p_1_ptr = asm(r: r_p_1) { + r: raw_ptr + }; + let r_p_2_ptr = asm(r: r_p_2) { + r: raw_ptr + }; assert(p_ptr == r_p_1_ptr); assert(p_ptr == r_p_2_ptr); - assert(p_ptr.read::<[u64;2]>() == p); + assert(p_ptr.read::<[u64; 2]>() == p); assert(*r_p_1 == *r_p_2); - + assert(r_p_1[0] == r_p_2[0]); assert(r_p_1[1] == r_p_2[1]); } #[inline(never)] -fn array_parameter_not_inlined(p: [u64;2]) { +fn array_parameter_not_inlined(p: [u64; 2]) { array_parameter(p) } -struct EmptyStruct { } +struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} #[inline(always)] fn empty_struct_parameter(p: EmptyStruct) { let r_p_1 = &p; let r_p_2 = &p; - - let p_ptr = asm(r: &p) { r: raw_ptr }; - let r_p_1_ptr = asm(r: r_p_1) { r: raw_ptr }; - let r_p_2_ptr = asm(r: r_p_2) { r: raw_ptr }; + + let p_ptr = asm(r: &p) { + r: raw_ptr + }; + let r_p_1_ptr = asm(r: r_p_1) { + r: raw_ptr + }; + let r_p_2_ptr = asm(r: r_p_2) { + r: raw_ptr + }; assert(p_ptr == r_p_1_ptr); assert(p_ptr == r_p_2_ptr); @@ -110,16 +149,21 @@ fn empty_struct_parameter_not_inlined(p: EmptyStruct) { #[inline(always)] fn struct_parameter(p: S) { - let r_p_1_addr_of = __addr_of(p); assert(r_p_1_addr_of == __addr_of(p)); let r_p_1 = &p; let r_p_2 = &p; - - let p_ptr = asm(r: &p) { r: raw_ptr }; - let r_p_1_ptr = asm(r: r_p_1) { r: raw_ptr }; - let r_p_2_ptr = asm(r: r_p_2) { r: raw_ptr }; + + let p_ptr = asm(r: &p) { + r: raw_ptr + }; + let r_p_1_ptr = asm(r: r_p_1) { + r: raw_ptr + }; + let r_p_2_ptr = asm(r: r_p_2) { + r: raw_ptr + }; assert(p_ptr == r_p_1_ptr); assert(p_ptr == r_p_2_ptr); @@ -127,7 +171,7 @@ fn struct_parameter(p: S) { assert(p_ptr.read::() == p); assert(*r_p_1 == *r_p_2); - + assert(r_p_1.x == r_p_2.x); let q = S::new(); @@ -139,20 +183,35 @@ fn struct_parameter_not_inlined(p: S) { struct_parameter(p) } +#[cfg(experimental_partial_eq = false)] impl Eq for (u64, u64) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u64, u64) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u64, u64) {} #[inline(always)] fn tuple_parameter(p: (u64, u64)) { let r_p_1 = &p; let r_p_2 = &p; - - let p_ptr = asm(r: &p) { r: raw_ptr }; - let r_p_1_ptr = asm(r: r_p_1) { r: raw_ptr }; - let r_p_2_ptr = asm(r: r_p_2) { r: raw_ptr }; + + let p_ptr = asm(r: &p) { + r: raw_ptr + }; + let r_p_1_ptr = asm(r: r_p_1) { + r: raw_ptr + }; + let r_p_2_ptr = asm(r: r_p_2) { + r: raw_ptr + }; assert(p_ptr == r_p_1_ptr); assert(p_ptr == r_p_2_ptr); @@ -160,7 +219,7 @@ fn tuple_parameter(p: (u64, u64)) { assert(p_ptr.read::<(u64, u64)>() == p); assert(*r_p_1 == *r_p_2); - + assert(r_p_1.0 == r_p_2.0); assert(r_p_1.1 == r_p_2.1); } @@ -174,6 +233,7 @@ enum E { A: u8, } +#[cfg(experimental_partial_eq = false)] impl Eq for E { fn eq(self, other: Self) -> bool { match (self, other) { @@ -181,15 +241,31 @@ impl Eq for E { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for E { + fn eq(self, other: Self) -> bool { + match (self, other) { + (E::A(r), E::A(l)) => r == l, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for E {} #[inline(always)] fn enum_parameter(p: E) { let r_p_1 = &p; let r_p_2 = &p; - - let p_ptr = asm(r: &p) { r: raw_ptr }; - let r_p_1_ptr = asm(r: r_p_1) { r: raw_ptr }; - let r_p_2_ptr = asm(r: r_p_2) { r: raw_ptr }; + + let p_ptr = asm(r: &p) { + r: raw_ptr + }; + let r_p_1_ptr = asm(r: r_p_1) { + r: raw_ptr + }; + let r_p_2_ptr = asm(r: r_p_2) { + r: raw_ptr + }; assert(p_ptr == r_p_1_ptr); assert(p_ptr == r_p_2_ptr); @@ -217,19 +293,28 @@ fn generic_parameter() { //generic_parameter_test(ptr_s); generic_parameter_test(S { x: 123u8 }); - generic_parameter_test(EmptyStruct { }); + generic_parameter_test(EmptyStruct {}); generic_parameter_test([123u64, 123u64]); generic_parameter_test(E::A(123u8)); } #[inline(always)] -fn generic_parameter_test(p: T) where T: Eq { +fn generic_parameter_test(p: T) +where + T: Eq, +{ let r_p_1 = &p; let r_p_2 = &p; - - let p_ptr = asm(r: &p) { r: raw_ptr }; - let r_p_1_ptr = asm(r: r_p_1) { r: raw_ptr }; - let r_p_2_ptr = asm(r: r_p_2) { r: raw_ptr }; + + let p_ptr = asm(r: &p) { + r: raw_ptr + }; + let r_p_1_ptr = asm(r: r_p_1) { + r: raw_ptr + }; + let r_p_2_ptr = asm(r: r_p_2) { + r: raw_ptr + }; assert(p_ptr == r_p_1_ptr); assert(p_ptr == r_p_2_ptr); @@ -248,7 +333,7 @@ fn generic_parameter_not_inlined() { fn test_all_inlined() { // u8_parameter(123u8); array_parameter([111u64, 222u64]); - empty_struct_parameter(EmptyStruct { }); + empty_struct_parameter(EmptyStruct {}); struct_parameter(S { x: 123u8 }); tuple_parameter((111u64, 222u64)); enum_parameter(E::A(123u8)); @@ -259,7 +344,7 @@ fn test_all_inlined() { fn test_not_inlined() { // u8_parameter_not_inlined(123u8); array_parameter_not_inlined([111u64, 222u64]); - empty_struct_parameter_not_inlined(EmptyStruct { }); + empty_struct_parameter_not_inlined(EmptyStruct {}); struct_parameter_not_inlined(S { x: 123u8 }); tuple_parameter_not_inlined((111u64, 222u64)); enum_parameter_not_inlined(E::A(123u8)); @@ -276,4 +361,4 @@ fn main() -> u64 { } #[inline(never)] -fn poke(_x: T) { } \ No newline at end of file +fn poke(_x: T) {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/test.partial_eq.toml.todo new file mode 100644 index 00000000000..bd94d0f0824 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_function_parameters/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/src/impls.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/src/impls.sw index 38feceeb687..d89dc353c2a 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/src/impls.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/src/impls.sw @@ -6,7 +6,8 @@ pub trait New { fn new() -> Self; } -pub trait ZeroSize { } +pub trait ZeroSize { +} impl New for bool { fn new() -> Self { @@ -56,6 +57,7 @@ impl New for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -69,10 +71,30 @@ impl Eq for str[6] { i = i + 1; }; - + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl New for str[6] { fn new() -> Self { @@ -80,13 +102,22 @@ impl New for str[6] { } } -impl Eq for [u64;2] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { fn eq(self, other: Self) -> bool { - self[0] == other[0] && self[1] == other[1] + self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} -impl New for [u64;2] { +impl New for [u64; 2] { fn new() -> Self { [123456, 654321] } @@ -96,11 +127,20 @@ pub struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl New for Struct { fn new() -> Self { @@ -108,26 +148,36 @@ impl New for Struct { } } -pub struct EmptyStruct { } +pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl New for EmptyStruct { fn new() -> Self { - EmptyStruct { } + EmptyStruct {} } } -impl ZeroSize for EmptyStruct { } +impl ZeroSize for EmptyStruct {} pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -135,6 +185,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl New for Enum { fn new() -> Self { @@ -142,11 +202,20 @@ impl New for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl New for (u8, u32) { fn new() -> Self { @@ -168,7 +237,9 @@ impl New for b256 { impl New for raw_ptr { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; + let null_ptr = asm() { + zero: raw_ptr + }; null_ptr.add::(42) } @@ -176,17 +247,28 @@ impl New for raw_ptr { impl New for raw_slice { fn new() -> Self { - let null_ptr = asm() { zero: raw_ptr }; - + let null_ptr = asm() { + zero: raw_ptr + }; + std::raw_slice::from_parts::(null_ptr, 42) } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl New for () { fn new() -> Self { @@ -194,25 +276,45 @@ impl New for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} -impl New for [u64;0] { +impl New for [u64; 0] { fn new() -> Self { [] } } -impl Eq for [u64;0] { +#[cfg(experimental_partial_eq = false)] +impl Eq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} -impl ZeroSize for [u64;0] { } +impl ZeroSize for [u64; 0] {} #[inline(never)] -fn poke(ref mut x: T) -> T { x } \ No newline at end of file +fn poke(ref mut x: T) -> T { + x +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/test.partial_eq.toml.todo new file mode 100644 index 00000000000..dfbe60f885e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_local_vars_and_values/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return", value = 42 } +expected_result_new_encoding = { action = "return_data", value = "000000000000002A" } +validate_abi = false +expected_warnings = 0 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/supertraits/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/supertraits/src/main.sw index a7df63ae6f8..fd1fd95f203 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/supertraits/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/supertraits/src/main.sw @@ -4,14 +4,13 @@ script; /// A /// | /// | -/// B +/// B /// /| /// / | /// C | /// \ | /// \| /// D -/// trait A { fn f(self) -> u64; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/src/main.sw index fd524024ea0..844dce9bd08 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/src/main.sw @@ -10,30 +10,39 @@ impl T1 for u64 { } } -impl T1 for (A, ) +impl T1 for (A, ) where - A: T1 + A: T1, { fn trait_fn() -> (A, ) { (A::trait_fn(), ) } } -fn f () -> T +fn f() -> T where - T: T1 + T: T1, { T::trait_fn() } -impl Eq for (u64,) { +#[cfg(experimental_partial_eq = false)] +impl Eq for (u64, ) { fn eq(self, other: Self) -> bool { self.0 == other.0 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u64, ) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u64, ) {} fn main() -> bool { - assert_eq(f::<(u64, )>(), (42,)); + assert_eq(f::<(u64, )>(), (42, )); true -} \ No newline at end of file +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/test.partial_eq.toml new file mode 100644 index 00000000000..7405926a369 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_nested/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "01" } +validate_abi = false +expected_warnings = 3 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..668a88f2e07 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "type": "bool" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/src/main.sw index 9917cef4408..52a33b3f240 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/src/main.sw @@ -2,17 +2,35 @@ script; use core::ops::*; +#[cfg(experimental_partial_eq = false)] impl Eq for (u64, u64, u64) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 && self.2 == other.2 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u64, u64, u64) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u64, u64, u64) {} +#[cfg(experimental_partial_eq = false)] impl Eq for (u64, u64) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u64, u64) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u64, u64) {} fn main() -> bool { let t = (42, 43); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/test.partial_eq.toml new file mode 100644 index 00000000000..37ce7922ea6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_trait/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "01" } +validate_abi = true +expected_warnings = 1 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/src/main.sw index c141e60547f..cc08b03be69 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/src/main.sw @@ -35,11 +35,20 @@ impl MyTypeAlias3 { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for MyTypeAlias2 { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for MyTypeAlias2 { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for MyTypeAlias2 {} struct GenericStruct { x: T, @@ -122,6 +131,7 @@ impl MyEnumTypeAlias3 { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for MyEnumTypeAlias2 { fn eq(self, other: Self) -> bool { match (self, other) { @@ -129,6 +139,16 @@ impl core::ops::Eq for MyEnumTypeAlias2 { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for MyEnumTypeAlias2 { + fn eq(self, other: Self) -> bool { + match (self, other) { + (MyEnumType::X(value1), MyEnumType::X(value2)) => value1 == value2, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for MyEnumTypeAlias2 {} fn enum_tests() { let x = ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000001); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/test.partial_eq.toml new file mode 100644 index 00000000000..25a20d4dbb7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/type_alias/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "" } +validate_abi = false +expected_warnings = 1 + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/src/huge_enum.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/src/huge_enum.sw index a3a5dcd3af9..98e7d181e88 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/src/huge_enum.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/src/huge_enum.sw @@ -208,6 +208,7 @@ impl Huge { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Huge { fn eq(self, other: Self) -> bool { match (self, other) { @@ -286,3 +287,77 @@ impl core::ops::Eq for Huge { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Huge { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Huge::a1, Huge::a1) => true, + (Huge::a2, Huge::a2) => true, + (Huge::a3, Huge::a3) => true, + (Huge::a4, Huge::a4) => true, + (Huge::a5, Huge::a5) => true, + (Huge::a6, Huge::a6) => true, + (Huge::a7, Huge::a7) => true, + (Huge::a8, Huge::a8) => true, + (Huge::b1, Huge::b1) => true, + (Huge::b2, Huge::b2) => true, + (Huge::b3, Huge::b3) => true, + (Huge::b4, Huge::b4) => true, + (Huge::b5, Huge::b5) => true, + (Huge::b6, Huge::b6) => true, + (Huge::b7, Huge::b7) => true, + (Huge::b8, Huge::b8) => true, + (Huge::c1, Huge::c1) => true, + (Huge::c2, Huge::c2) => true, + (Huge::c3, Huge::c3) => true, + (Huge::c4, Huge::c4) => true, + (Huge::c5, Huge::c5) => true, + (Huge::c6, Huge::c6) => true, + (Huge::c7, Huge::c7) => true, + (Huge::c8, Huge::c8) => true, + (Huge::d1, Huge::d1) => true, + (Huge::d2, Huge::d2) => true, + (Huge::d3, Huge::d3) => true, + (Huge::d4, Huge::d4) => true, + (Huge::d5, Huge::d5) => true, + (Huge::d6, Huge::d6) => true, + (Huge::d7, Huge::d7) => true, + (Huge::d8, Huge::d8) => true, + (Huge::e1, Huge::e1) => true, + (Huge::e2, Huge::e2) => true, + (Huge::e3, Huge::e3) => true, + (Huge::e4, Huge::e4) => true, + (Huge::e5, Huge::e5) => true, + (Huge::e6, Huge::e6) => true, + (Huge::e7, Huge::e7) => true, + (Huge::e8, Huge::e8) => true, + (Huge::f1, Huge::f1) => true, + (Huge::f2, Huge::f2) => true, + (Huge::f3, Huge::f3) => true, + (Huge::f4, Huge::f4) => true, + (Huge::f5, Huge::f5) => true, + (Huge::f6, Huge::f6) => true, + (Huge::f7, Huge::f7) => true, + (Huge::f8, Huge::f8) => true, + (Huge::g1, Huge::g1) => true, + (Huge::g2, Huge::g2) => true, + (Huge::g3, Huge::g3) => true, + (Huge::g4, Huge::g4) => true, + (Huge::g5, Huge::g5) => true, + (Huge::g6, Huge::g6) => true, + (Huge::g7, Huge::g7) => true, + (Huge::g8, Huge::g8) => true, + (Huge::h1, Huge::h1) => true, + (Huge::h2, Huge::h2) => true, + (Huge::h3, Huge::h3) => true, + (Huge::h4, Huge::h4) => true, + (Huge::h5, Huge::h5) => true, + (Huge::h6, Huge::h6) => true, + (Huge::h7, Huge::h7) => true, + (Huge::h8, Huge::h8) => true, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Huge {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/test.partial_eq.toml new file mode 100644 index 00000000000..e38e1084d0f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/chess/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "0000000000000011" } +validate_abi = false +expected_warnings = 2 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/src/main.sw index 3ceb317ffdc..637a97415d9 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/src/main.sw @@ -7,6 +7,7 @@ enum Error { U8Error: u8, } +#[cfg(experimental_partial_eq = false)] impl Eq for Error { fn eq(self, other: Self) -> bool { match (self, other) { @@ -16,15 +17,52 @@ impl Eq for Error { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Error { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Error::BoolError(val1), Error::BoolError(val2)) => val2 == val1, + (Error::U8Error(val1), Error::U8Error(val2)) => val2 == val1, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Error {} + +fn test_none_ok_or_eq(_val: T, default: E) +where + E: Eq, +{ + match None::.ok_or(default) { + Ok(_) => revert(0), + Err(e) => assert(default == e), + } +} -fn test_none_ok_or(_val: T, default: E) where E: Eq { +#[cfg(experimental_partial_eq = true)] +fn test_none_ok_or_partial_eq(_val: T, default: E) +where + E: Eq, +{ match None::.ok_or(default) { Ok(_) => revert(0), Err(e) => assert(default == e), } } +#[cfg(experimental_partial_eq = false)] +fn test() { + test_none_ok_or_eq(true, Error::BoolError(true)); +} + +#[cfg(experimental_partial_eq = true)] +fn test() { + test_none_ok_or_eq(true, Error::BoolError(true)); + test_none_ok_or_partial_eq(true, Error::BoolError(true)); +} + fn main() -> bool { - test_none_ok_or(true, Error::BoolError(true)); + test(); return true; } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/test.partial_eq.toml.todo new file mode 100644 index 00000000000..d0bbe269111 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_custom_type/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "01" } +validate_abi = true +expected_warnings = 2 + +experimental = { new_encoding = true, partial_eq = true } + diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..aab161b0506 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "type": "()" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/src/main.sw index cfc604350ac..bde9e045fdd 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/src/main.sw @@ -2,11 +2,32 @@ script; use core::ops::*; -fn test_ok_or(val: T, default: E) where T: Eq { +fn test_ok_or_eq(val: T, default: E) where T: Eq { match Some(val).ok_or(default) { Ok(inner) => assert(inner == val), Err(_) => revert(0), }; } -fn main() {} +#[cfg(experimental_partial_eq = true)] +fn test_ok_or_partial_eq(val: T, default: E) where T: PartialEq { + match Some(val).ok_or(default) { + Ok(inner) => assert(inner == val), + Err(_) => revert(0), + }; +} + +#[cfg(experimental_partial_eq = false)] +fn test() { + test_ok_or_eq(0, 0u8); +} + +#[cfg(experimental_partial_eq = true)] +fn test() { + test_ok_or_eq(0, 0u8); + test_ok_or_partial_eq(0, 0u8); +} + +fn main() { + test(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/test.partial_eq.toml new file mode 100644 index 00000000000..1bddf2066d9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/eq_generic/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "run" +expected_result = { action = "return_data", value = "" } +validate_abi = true +expected_warnings = 1 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..668a88f2e07 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "type": "bool" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/src/data_structures.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/src/data_structures.sw index b943b820e31..e20cd0851a8 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/src/data_structures.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/src/data_structures.sw @@ -16,11 +16,20 @@ pub enum MyEnum { Y: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for MyStruct { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for MyStruct { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for MyStruct {} impl Hash for MyStruct { fn hash(self, ref mut state: Hasher) { @@ -29,6 +38,7 @@ impl Hash for MyStruct { } } +#[cfg(experimental_partial_eq = false)] impl Eq for MyEnum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -38,6 +48,18 @@ impl Eq for MyEnum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for MyEnum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (MyEnum::X(val1), MyEnum::X(val2)) => val1 == val2, + (MyEnum::Y(val1), MyEnum::Y(val2)) => val1 == val2, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for MyEnum {} impl Hash for MyEnum { fn hash(self, ref mut state: Hasher) { @@ -54,17 +76,35 @@ impl Hash for MyEnum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u64, u64) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u64, u64) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u64, u64) {} +#[cfg(experimental_partial_eq = false)] impl Eq for [u64; 3] { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] && self[2] == other[2] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 3] {} ///////////////////////////////////////////////////////////////////////////// // Error @@ -82,6 +122,7 @@ pub enum Error { StringError: str, } +#[cfg(experimental_partial_eq = false)] impl Eq for Error { fn eq(self, other: Self) -> bool { match (self, other) { @@ -98,6 +139,25 @@ impl Eq for Error { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Error { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Error::BoolError(val1), Error::BoolError(val2)) => val1 == val2, + (Error::U8Error(val1), Error::U8Error(val2)) => val1 == val2, + (Error::U16Error(val1), Error::U16Error(val2)) => val1 == val2, + (Error::U32Error(val1), Error::U32Error(val2)) => val1 == val2, + (Error::U64Error(val1), Error::U64Error(val2)) => val1 == val2, + (Error::StructError(val1), Error::StructError(val2)) => val1 == val2, + (Error::EnumError(val1), Error::EnumError(val2)) => val1 == val2, + (Error::TupleError(val1), Error::TupleError(val2)) => val1 == val2, + (Error::StringError(val1), Error::StringError(val2)) => sha256(val1) == sha256(val2), + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Error {} impl Hash for Error { fn hash(self, ref mut state: Hasher) { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/test.partial_eq.toml.todo new file mode 100644 index 00000000000..5fc1955fff0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/test.partial_eq.toml.todo @@ -0,0 +1,8 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "01" } +validate_abi = true + +expected_warnings = 12 + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..668a88f2e07 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "type": "bool" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/src/main.sw index f3728eda5ef..19a4846b586 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/src/main.sw @@ -10,32 +10,69 @@ enum MyEnum { } pub type StringArray = str[4]; +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for StringArray { fn eq(self, other: Self) -> bool { from_str_array(self) == from_str_array(other) } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for StringArray { + fn eq(self, other: Self) -> bool { + from_str_array(self) == from_str_array(other) + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for StringArray {} pub type Array = [u32; 2]; +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Array { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Array { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Array {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for MyStruct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for MyStruct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for MyStruct {} pub type Tuple = (u32, u32); +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Tuple { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Tuple { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Tuple {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for MyEnum { fn eq(self, other: MyEnum) -> bool { match (self, other) { @@ -45,6 +82,18 @@ impl core::ops::Eq for MyEnum { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for MyEnum { + fn eq(self, other: MyEnum) -> bool { + match (self, other) { + (MyEnum::A(inner1), MyEnum::A(inner2)) => inner1 == inner2, + (MyEnum::B(inner1), MyEnum::B(inner2)) => inner1 == inner2, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for MyEnum {} fn main() -> bool { // Test with u8 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/test.partial_eq.toml new file mode 100644 index 00000000000..8798dc99500 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option_eq/test.partial_eq.toml @@ -0,0 +1,7 @@ +category = "run" +expected_result = { action = "return_data", value = "01" } +validate_abi = true + +expected_warnings = 5 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..668a88f2e07 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/json_abi_oracle.partial_eq.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "type": "bool" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/data_structures.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/data_structures.sw index f4ddf6342fe..97809c0a63e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/data_structures.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/data_structures.sw @@ -16,12 +16,22 @@ pub enum MyEnum { Y: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for MyStruct { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for MyStruct { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for MyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for MyEnum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -31,21 +41,60 @@ impl Eq for MyEnum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for MyEnum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (MyEnum::X(val1), MyEnum::X(val2)) => val1 == val2, + (MyEnum::Y(val1), MyEnum::Y(val2)) => val1 == val2, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for MyEnum {} +#[cfg(experimental_partial_eq = false)] impl Eq for (u64, u64) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u64, u64) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u64, u64) {} +#[cfg(experimental_partial_eq = false)] impl Eq for [u64; 3] { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] && self[2] == other[2] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 3] {} +#[cfg(experimental_partial_eq = false)] impl Eq for str[4] { fn eq(self, other: Self) -> bool { sha256_str_array(self) == sha256_str_array(other) } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[4] { + fn eq(self, other: Self) -> bool { + sha256_str_array(self) == sha256_str_array(other) + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for str[4] {} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/test.partial_eq.toml.todo b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/test.partial_eq.toml.todo new file mode 100644 index 00000000000..0f0657bba2b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/test.partial_eq.toml.todo @@ -0,0 +1,7 @@ +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6898 is fixed. +category = "run" +expected_result = { action = "return_data", value = "01" } +validate_abi = true +expected_warnings = 7 + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/contract_with_type_aliases_abi/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/contract_with_type_aliases_abi/src/main.sw index 4add850369e..e31c06072ac 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/contract_with_type_aliases_abi/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/contract_with_type_aliases_abi/src/main.sw @@ -9,37 +9,80 @@ pub struct IdentityAliasWrapper { } pub type Array = [IdentityAlias; 2]; +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Array { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Array { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Array {} pub type Tuple = (SubId, SubId); +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Tuple { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Tuple { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Tuple {} pub type StringTy = str[9]; +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for StringTy { fn eq(self, other: Self) -> bool { sha256_str_array(self) == sha256_str_array(other) } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for StringTy { + fn eq(self, other: Self) -> bool { + sha256_str_array(self) == sha256_str_array(other) + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for StringTy {} pub type IdentityAliasWrapperAlias = IdentityAliasWrapper; +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for IdentityAliasWrapperAlias { fn eq(self, other: Self) -> bool { self.i == other.i } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for IdentityAliasWrapperAlias { + fn eq(self, other: Self) -> bool { + self.i == other.i + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for IdentityAliasWrapperAlias {} pub struct Generic { pub f: T, } abi MyContract { - fn foo(x: SubId, y: [IdentityAlias; 2], z: IdentityAliasWrapperAlias, w: Generic, u: (SubId, SubId), s: StringTy) -> (SubId, [IdentityAlias; 2], IdentityAliasWrapperAlias, Generic, (SubId, SubId), StringTy); + fn foo( + x: SubId, + y: [IdentityAlias; 2], + z: IdentityAliasWrapperAlias, + w: Generic, + u: (SubId, SubId), + s: StringTy, + ) -> (SubId, [IdentityAlias; 2], IdentityAliasWrapperAlias, Generic, (SubId, SubId), StringTy); } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..e687a917725 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_abi_oracle.partial_eq.json @@ -0,0 +1,202 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "a28cf4bd3deddbee8bde0daa7b4534d37db682c73dc398fd720c75f3775a3df0", + "metadataTypeId": 1, + "type": "(b256, [enum std::identity::Identity; 2], struct contract_with_type_aliases_abi::IdentityAliasWrapper, struct contract_with_type_aliases_abi::Generic, (b256, b256), str[9])" + }, + { + "concreteTypeId": "bc42ec26f2a17c5b23fc007d96b96bf35f8949ee156afdd789064210d881f7e0", + "metadataTypeId": 0, + "type": "(b256, b256)" + }, + { + "concreteTypeId": "2771ee6889dff227342d90c5f1ea6019ad8c8de50880048f823e1b926b26c13e", + "metadataTypeId": 2, + "type": "[enum std::identity::Identity; 2]" + }, + { + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "type": "b256" + }, + { + "concreteTypeId": "bc5b9d5b85b9fb6e78bbb779cc67a95e60d6f291cd256a4c7dba66308dc7dfb8", + "type": "str[9]" + }, + { + "concreteTypeId": "3981e3d30e52d053c35da213685243d8c82ef363f58bfc2d14986c393254c450", + "metadataTypeId": 5, + "type": "struct contract_with_type_aliases_abi::Generic", + "typeArguments": [ + "1567acc73497bb83e04c376e2c8d3997d0207f0b9b8fa17b0192ee62e1c7a60b" + ] + }, + { + "concreteTypeId": "1567acc73497bb83e04c376e2c8d3997d0207f0b9b8fa17b0192ee62e1c7a60b", + "metadataTypeId": 6, + "type": "struct contract_with_type_aliases_abi::IdentityAliasWrapper" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [ + { + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "name": "x" + }, + { + "concreteTypeId": "2771ee6889dff227342d90c5f1ea6019ad8c8de50880048f823e1b926b26c13e", + "name": "y" + }, + { + "concreteTypeId": "1567acc73497bb83e04c376e2c8d3997d0207f0b9b8fa17b0192ee62e1c7a60b", + "name": "z" + }, + { + "concreteTypeId": "3981e3d30e52d053c35da213685243d8c82ef363f58bfc2d14986c393254c450", + "name": "w" + }, + { + "concreteTypeId": "bc42ec26f2a17c5b23fc007d96b96bf35f8949ee156afdd789064210d881f7e0", + "name": "u" + }, + { + "concreteTypeId": "bc5b9d5b85b9fb6e78bbb779cc67a95e60d6f291cd256a4c7dba66308dc7dfb8", + "name": "s" + } + ], + "name": "foo", + "output": "a28cf4bd3deddbee8bde0daa7b4534d37db682c73dc398fd720c75f3775a3df0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [ + { + "components": [ + { + "name": "__tuple_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "name": "__tuple_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ], + "metadataTypeId": 0, + "type": "(_, _)" + }, + { + "components": [ + { + "name": "__tuple_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "name": "__tuple_element", + "typeId": 2 + }, + { + "name": "__tuple_element", + "typeId": 6 + }, + { + "name": "__tuple_element", + "typeArguments": [ + { + "name": "", + "typeId": 6 + } + ], + "typeId": 5 + }, + { + "name": "__tuple_element", + "typeId": 0 + }, + { + "name": "__tuple_element", + "typeId": "bc5b9d5b85b9fb6e78bbb779cc67a95e60d6f291cd256a4c7dba66308dc7dfb8" + } + ], + "metadataTypeId": 1, + "type": "(_, _, _, _, _, _)" + }, + { + "components": [ + { + "name": "__array_element", + "typeId": 3 + } + ], + "metadataTypeId": 2, + "type": "[_; 2]" + }, + { + "components": [ + { + "name": "Address", + "typeId": 7 + }, + { + "name": "ContractId", + "typeId": 8 + } + ], + "metadataTypeId": 3, + "type": "enum std::identity::Identity" + }, + { + "metadataTypeId": 4, + "type": "generic T" + }, + { + "components": [ + { + "name": "f", + "typeId": 4 + } + ], + "metadataTypeId": 5, + "type": "struct contract_with_type_aliases_abi::Generic", + "typeParameters": [ + 4 + ] + }, + { + "components": [ + { + "name": "i", + "typeId": 3 + } + ], + "metadataTypeId": 6, + "type": "struct contract_with_type_aliases_abi::IdentityAliasWrapper" + }, + { + "components": [ + { + "name": "bits", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ], + "metadataTypeId": 7, + "type": "struct std::address::Address" + }, + { + "components": [ + { + "name": "bits", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ], + "metadataTypeId": 8, + "type": "struct std::contract_id::ContractId" + } + ], + "programType": "contract", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_storage_slots_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_storage_slots_oracle.partial_eq.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/json_storage_slots_oracle.partial_eq.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/test.partial_eq.toml new file mode 100644 index 00000000000..e531c46c642 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/contract_with_type_aliases/test.partial_eq.toml @@ -0,0 +1,5 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true + +experimental = { new_encoding = true, partial_eq = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.partial_eq.json new file mode 100644 index 00000000000..6da9ef90fa3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_abi_oracle.partial_eq.json @@ -0,0 +1,52 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "41bd1a98f0a59642d8f824c805b798a5f268d1f7d05808eb05c4189c493f1be0", + "metadataTypeId": 0, + "type": "(u64, u64)" + }, + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "name": "a" + }, + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "name": "b" + } + ], + "name": "multiply_u64", + "output": "41bd1a98f0a59642d8f824c805b798a5f268d1f7d05808eb05c4189c493f1be0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [ + { + "components": [ + { + "name": "__tuple_element", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "__tuple_element", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "metadataTypeId": 0, + "type": "(_, _)" + } + ], + "programType": "contract", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_storage_slots_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_storage_slots_oracle.partial_eq.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_storage_slots_oracle.partial_eq.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw index c18e6898cfc..3c1d0fa54b4 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw @@ -20,13 +20,23 @@ pub struct U128Duplicate { pub trait AltFrom { fn from(h: u64, l: u64) -> Self; } { + } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for U128Duplicate { fn eq(self, other: Self) -> bool { self.lower == other.lower && self.upper == other.upper } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for U128Duplicate { + fn eq(self, other: Self) -> bool { + self.lower == other.lower && self.upper == other.upper + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for U128Duplicate {} /// Function for creating U128 from its u64 components impl AltFrom for U128Duplicate { @@ -94,9 +104,11 @@ impl U128Duplicate { // Multiply two u64 values, producing a U128 pub fn mul64(a: u64, b: u64) -> U128Duplicate { // Split a and b into 32-bit lo and hi components - let a_lo = (a & 0x00000000ffffffff).try_as_u32().unwrap(); + let a_lo = (a + & 0x00000000ffffffff).try_as_u32().unwrap(); let a_hi = (a >> 32).try_as_u32().unwrap(); - let b_lo = (b & 0x00000000ffffffff).try_as_u32().unwrap(); + let b_lo = (b + & 0x00000000ffffffff).try_as_u32().unwrap(); let b_hi = (b >> 32).try_as_u32().unwrap(); // Calculate low, high, and mid multiplications @@ -106,13 +118,7 @@ pub fn mul64(a: u64, b: u64) -> U128Duplicate { let ab_lo = (a_lo * b_lo).as_u64(); // Calculate the carry bit - let carry_bit = ( - ( - ab_mid.try_as_u32().unwrap() + - ba_mid.try_as_u32().unwrap() + - (ab_lo >> 32).try_as_u32().unwrap() - ) >> 32 - ).as_u64(); + let carry_bit = ((ab_mid.try_as_u32().unwrap() + ba_mid.try_as_u32().unwrap() + (ab_lo >> 32).try_as_u32().unwrap()) >> 32).as_u64(); // low result is what's left after the (overflowing) multiplication of a and b let result_lo: u64 = a * b; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/test.partial_eq.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/test.partial_eq.toml new file mode 100644 index 00000000000..b7a642d4c35 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/test.partial_eq.toml @@ -0,0 +1,6 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true +expected_warnings = 2 + +experimental = { new_encoding = true, partial_eq = true } \ No newline at end of file diff --git a/test/src/in_language_tests/Forc.toml b/test/src/in_language_tests/Forc.toml index 583c8fbc13e..9a0fb329eb6 100644 --- a/test/src/in_language_tests/Forc.toml +++ b/test/src/in_language_tests/Forc.toml @@ -29,7 +29,9 @@ members = [ "test_programs/result_inline_tests", "test_programs/revert_inline_tests", "test_programs/storage_key_inline_tests", - "test_programs/storage_vec_iter_tests", +# TODO: Enable this test once https://github.com/FuelLabs/sway/issues/6899 is fixed. +# Compilation fails with error: No method "eq(Self, Self) -> ()" found for type "Self". +# "test_programs/storage_vec_iter_tests", "test_programs/string_inline_tests", "test_programs/tx_inline_tests", "test_programs/u128_inline_tests", diff --git a/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/impls.sw b/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/impls.sw index 4790535ef4a..b92f295e101 100644 --- a/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/impls.sw +++ b/test/src/in_language_tests/test_programs/storage_vec_iter_tests/src/impls.sw @@ -116,6 +116,7 @@ impl TestInstance for str { } } +#[cfg(experimental_partial_eq = false)] impl Eq for str[6] { fn eq(self, other: Self) -> bool { let mut i = 0; @@ -133,6 +134,26 @@ impl Eq for str[6] { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for str[6] { + fn eq(self, other: Self) -> bool { + let mut i = 0; + while i < 6 { + let ptr_self = __addr_of(self).add::(i); + let ptr_other = __addr_of(other).add::(i); + + if ptr_self.read::() != ptr_other.read::() { + return false; + } + + i = i + 1; + }; + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for str[6] {} impl TestInstance for str[6] { fn elements(len: u64) -> Vec { @@ -152,11 +173,20 @@ impl TestInstance for str[6] { } } +#[cfg(experimental_partial_eq = false)] impl Eq for [u64; 2] { fn eq(self, other: Self) -> bool { self[0] == other[0] && self[1] == other[1] } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 2] {} impl TestInstance for [u64; 2] { fn elements(len: u64) -> Vec { @@ -179,11 +209,20 @@ pub struct Struct { x: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Struct { fn eq(self, other: Self) -> bool { self.x == other.x } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Struct { + fn eq(self, other: Self) -> bool { + self.x == other.x + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Struct {} impl TestInstance for Struct { fn elements(len: u64) -> Vec { @@ -202,11 +241,20 @@ impl TestInstance for Struct { pub struct EmptyStruct {} +#[cfg(experimental_partial_eq = false)] impl Eq for EmptyStruct { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for EmptyStruct { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for EmptyStruct {} impl TestInstance for EmptyStruct { fn elements(len: u64) -> Vec { @@ -224,6 +272,7 @@ pub enum Enum { A: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for Enum { fn eq(self, other: Self) -> bool { match (self, other) { @@ -231,6 +280,16 @@ impl Eq for Enum { } } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Enum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Enum::A(l), Enum::A(r)) => l == r, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Enum {} impl TestInstance for Enum { fn elements(len: u64) -> Vec { @@ -245,11 +304,20 @@ impl TestInstance for Enum { } } +#[cfg(experimental_partial_eq = false)] impl Eq for (u8, u32) { fn eq(self, other: Self) -> bool { self.0 == other.0 && self.1 == other.1 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for (u8, u32) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for (u8, u32) {} impl TestInstance for (u8, u32) { fn elements(len: u64) -> Vec { @@ -328,11 +396,20 @@ impl TestInstance for raw_slice { } } +#[cfg(experimental_partial_eq = false)] impl Eq for raw_slice { fn eq(self, other: Self) -> bool { self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for raw_slice { + fn eq(self, other: Self) -> bool { + self.ptr() == other.ptr() && self.number_of_bytes() == other.number_of_bytes() + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for raw_slice {} impl TestInstance for () { fn elements(len: u64) -> Vec { @@ -346,11 +423,20 @@ impl TestInstance for () { } } +#[cfg(experimental_partial_eq = false)] impl Eq for () { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for () { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for () {} impl TestInstance for [u64; 0] { fn elements(len: u64) -> Vec { @@ -364,8 +450,17 @@ impl TestInstance for [u64; 0] { } } +#[cfg(experimental_partial_eq = false)] impl Eq for [u64; 0] { fn eq(self, other: Self) -> bool { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for [u64; 0] { + fn eq(self, other: Self) -> bool { + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for [u64; 0] {} diff --git a/test/src/sdk-harness/test_projects/private_struct_fields_in_storage_and_abi/src/lib.sw b/test/src/sdk-harness/test_projects/private_struct_fields_in_storage_and_abi/src/lib.sw index 3d3710a86ec..74d74f5b758 100644 --- a/test/src/sdk-harness/test_projects/private_struct_fields_in_storage_and_abi/src/lib.sw +++ b/test/src/sdk-harness/test_projects/private_struct_fields_in_storage_and_abi/src/lib.sw @@ -11,11 +11,20 @@ impl CanInitStruct { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for CanInitStruct { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for CanInitStruct { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for CanInitStruct {} pub struct CannotInitStruct { pub x: u64, @@ -29,8 +38,17 @@ impl CannotInitStruct { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for CannotInitStruct { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for CannotInitStruct { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for CannotInitStruct {} diff --git a/test/src/sdk-harness/test_projects/storage_access/src/main.sw b/test/src/sdk-harness/test_projects/storage_access/src/main.sw index 41c223e4bd1..ef3ad9fb96b 100644 --- a/test/src/sdk-harness/test_projects/storage_access/src/main.sw +++ b/test/src/sdk-harness/test_projects/storage_access/src/main.sw @@ -28,23 +28,50 @@ struct Simple { w: u64, } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for M { fn eq(self, other: Self) -> bool { self.u == other.u && self.v == other.v } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for M { + fn eq(self, other: Self) -> bool { + self.u == other.u && self.v == other.v + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for M {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for T { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y && self.z == other.z } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for T { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y && self.z == other.z + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for T {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for S { fn eq(self, other: Self) -> bool { self.a == other.a && self.b == other.b && self.c == other.c && self.d == other.d } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for S { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b && self.c == other.c && self.d == other.d + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for S {} struct S2 { map0: StorageMap, diff --git a/test/src/sdk-harness/test_projects/storage_init/src/main.sw b/test/src/sdk-harness/test_projects/storage_init/src/main.sw index 696f84b1ef1..f0c5068a6af 100644 --- a/test/src/sdk-harness/test_projects/storage_init/src/main.sw +++ b/test/src/sdk-harness/test_projects/storage_init/src/main.sw @@ -54,24 +54,52 @@ impl F { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for T { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y && self.z == other.z && self.boolean == other.boolean && self.int8 == other.int8 && self.int16 == other.int16 && self.int32 == other.int32 } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for T { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y && self.z == other.z && self.boolean == other.boolean && self.int8 == other.int8 && self.int16 == other.int16 && self.int32 == other.int32 + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for T {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for S { fn eq(self, other: Self) -> bool { self.x == other.x && self.y == other.y && self.z == other.z && self.u == other.u && self.t == other.t && self.f_int8.equals(other.f_int8) && self.f_int64.equals(other.f_int64) && self.f_tuple.equals(other.f_tuple) } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for S { + fn eq(self, other: Self) -> bool { + self.x == other.x && self.y == other.y && self.z == other.z && self.u == other.u && self.t == other.t && self.f_int8.equals(other.f_int8) && self.f_int64.equals(other.f_int64) && self.f_tuple.equals(other.f_tuple) + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for S {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for F { fn eq(self, other: Self) -> bool { self.equals(other) } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for F { + fn eq(self, other: Self) -> bool { + self.equals(other) + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for F {} +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for E { fn eq(self, other: Self) -> bool { match (self, other) { @@ -87,6 +115,24 @@ impl core::ops::Eq for E { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for E { + fn eq(self, other: Self) -> bool { + match (self, other) { + (E::A(l), E::A(r)) => l == r, + (E::B(l), E::B(r)) => l == r, + (E::Int8(l), E::Int8(r)) => l == r, + (E::Int16(l), E::Int16(r)) => l == r, + (E::Int32(l), E::Int32(r)) => l == r, + (E::Bool(l), E::Bool(r)) => l == r, + (E::Unit, E::Unit) => true, + (E::Enum(l), E::Enum(r)) => l.equals(r), + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for E {} storage { x: u64 = 64, diff --git a/test/src/sdk-harness/test_projects/storage_map_nested/src/main.sw b/test/src/sdk-harness/test_projects/storage_map_nested/src/main.sw index 3c83ef01cdd..ac2185330cb 100644 --- a/test/src/sdk-harness/test_projects/storage_map_nested/src/main.sw +++ b/test/src/sdk-harness/test_projects/storage_map_nested/src/main.sw @@ -7,11 +7,20 @@ struct M { v: u64, } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for M { fn eq(self, other: Self) -> bool { self.u == other.u && self.v == other.v } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for M { + fn eq(self, other: Self) -> bool { + self.u == other.u && self.v == other.v + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for M {} impl Hash for M { fn hash(self, ref mut state: Hasher) { @@ -25,6 +34,7 @@ pub enum E { B: b256, } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for E { fn eq(self, other: Self) -> bool { match (self, other) { @@ -34,6 +44,18 @@ impl core::ops::Eq for E { } } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for E { + fn eq(self, other: Self) -> bool { + match (self, other) { + (E::A(l), E::A(r)) => l == r, + (E::B(l), E::B(r)) => l == r, + _ => false, + } + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for E {} impl Hash for str[4] { fn hash(self, ref mut state: Hasher) { diff --git a/test/src/sdk-harness/test_projects/storage_vec_to_vec/Forc.lock b/test/src/sdk-harness/test_projects/storage_vec_to_vec/Forc.lock new file mode 100644 index 00000000000..8defc659d09 --- /dev/null +++ b/test/src/sdk-harness/test_projects/storage_vec_to_vec/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-2B98DB2C67784422" + +[[package]] +name = "std" +source = "path+from-root-2B98DB2C67784422" +dependencies = ["core"] + +[[package]] +name = "storage_vec_to_vec" +source = "member" +dependencies = ["std"] diff --git a/test/src/sdk-harness/test_projects/storage_vec_to_vec/src/main.sw b/test/src/sdk-harness/test_projects/storage_vec_to_vec/src/main.sw index 9e2f3241030..40ce6f9dd5e 100644 --- a/test/src/sdk-harness/test_projects/storage_vec_to_vec/src/main.sw +++ b/test/src/sdk-harness/test_projects/storage_vec_to_vec/src/main.sw @@ -8,11 +8,20 @@ pub struct TestStruct { val3: u64, } +#[cfg(experimental_partial_eq = false)] impl Eq for TestStruct { fn eq(self, other: Self) -> bool { self.val1 == other.val1 && self.val2 == other.val2 && self.val3 == other.val3 } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for TestStruct { + fn eq(self, other: Self) -> bool { + self.val1 == other.val1 && self.val2 == other.val2 && self.val3 == other.val3 + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for TestStruct {} storage { storage_vec_u64: StorageVec = StorageVec {}, From 1e5dd50d7b5f015a1a43beba0a165d0a7de6adf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Ron=C4=8Devi=C4=87?= Date: Thu, 6 Feb 2025 20:09:58 +0100 Subject: [PATCH 2/3] Fix typos --- forc-plugins/forc-migrate/src/matching/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forc-plugins/forc-migrate/src/matching/mod.rs b/forc-plugins/forc-migrate/src/matching/mod.rs index 70b1baba246..c328e7c9582 100644 --- a/forc-plugins/forc-migrate/src/matching/mod.rs +++ b/forc-plugins/forc-migrate/src/matching/mod.rs @@ -26,7 +26,7 @@ //! [Clippy](https://doc.rust-lang.org/clippy/), `forc migrate` is significantly //! different. Instead of providing hundreds of independent lints that //! automatically check for localized issues, migrations provide only a handful -//! of migration steps, that are orchestrated withing a single migration process, +//! of migration steps, that are orchestrated within a single migration process, //! some of them possibly being interactive. //! //! Each migration step, in general, wants to take a look at a larger scope at a time, From 1b4b4528ad83782fd3187a1999a331c7c62315aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Ron=C4=8Devi=C4=87?= Date: Thu, 6 Feb 2025 23:15:28 +0100 Subject: [PATCH 3/3] Implement `PartialEq` for types in `std::crypto` --- sway-lib-std/src/crypto/ed25519.sw | 17 ++++++++++++ sway-lib-std/src/crypto/message.sw | 22 +++++++++++++++ sway-lib-std/src/crypto/point2d.sw | 27 +++++++++++++++++++ sway-lib-std/src/crypto/public_key.sw | 22 +++++++++++++++ sway-lib-std/src/crypto/scalar.sw | 22 +++++++++++++++ sway-lib-std/src/crypto/secp256k1.sw | 17 ++++++++++++ sway-lib-std/src/crypto/secp256r1.sw | 17 ++++++++++++ .../test_projects/tx_fields/mod.rs | 2 -- 8 files changed, 144 insertions(+), 2 deletions(-) diff --git a/sway-lib-std/src/crypto/ed25519.sw b/sway-lib-std/src/crypto/ed25519.sw index c8c57c5538c..d59a88d760a 100644 --- a/sway-lib-std/src/crypto/ed25519.sw +++ b/sway-lib-std/src/crypto/ed25519.sw @@ -189,6 +189,7 @@ impl Into for Ed25519 { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Ed25519 { fn eq(self, other: Self) -> bool { let mut iter = 0; @@ -202,6 +203,22 @@ impl core::ops::Eq for Ed25519 { true } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Ed25519 { + fn eq(self, other: Self) -> bool { + let mut iter = 0; + while iter < 64 { + if self.bits[iter] != other.bits[iter] { + return false; + } + iter += 1; + } + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Ed25519 {} impl Hash for Ed25519 { fn hash(self, ref mut state: Hasher) { diff --git a/sway-lib-std/src/crypto/message.sw b/sway-lib-std/src/crypto/message.sw index 811051c02fa..184fe455eed 100644 --- a/sway-lib-std/src/crypto/message.sw +++ b/sway-lib-std/src/crypto/message.sw @@ -83,6 +83,7 @@ impl TryInto for Message { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Message { fn eq(self, other: Self) -> bool { if self.bytes.len() != other.bytes.len() { @@ -101,6 +102,27 @@ impl core::ops::Eq for Message { true } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Message { + fn eq(self, other: Self) -> bool { + if self.bytes.len() != other.bytes.len() { + return false; + } + + let mut iter = 0; + while iter < self.bytes.len() { + if self.bytes.get(iter).unwrap() != other.bytes.get(iter).unwrap() + { + return false; + } + iter += 1; + } + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Message {} impl Hash for Message { fn hash(self, ref mut state: Hasher) { diff --git a/sway-lib-std/src/crypto/point2d.sw b/sway-lib-std/src/crypto/point2d.sw index 2d3e29fc970..39c9249c4d3 100644 --- a/sway-lib-std/src/crypto/point2d.sw +++ b/sway-lib-std/src/crypto/point2d.sw @@ -17,6 +17,7 @@ pub struct Point2D { y: Bytes, } +#[cfg(experimental_partial_eq = false)] impl Eq for Point2D { fn eq(self, other: Self) -> bool { // All points must be of length 32 @@ -41,6 +42,32 @@ impl Eq for Point2D { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Point2D { + fn eq(self, other: Self) -> bool { + if self.x.len() != 32 + || self.y.len() != 32 + || other.x.len() != 32 + || other.y.len() != 32 + { + return false; + } + + let mut iter = 0; + while iter < 32 { + if self.x.get(iter).unwrap() != other.x.get(iter).unwrap() { + return false; + } else if self.y.get(iter).unwrap() != other.y.get(iter).unwrap() { + return false; + } + + iter += 1; + } + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Point2D {} impl Point2D { /// Returns a new, uninitialized Point2D. diff --git a/sway-lib-std/src/crypto/public_key.sw b/sway-lib-std/src/crypto/public_key.sw index b940395ebf0..868ab242093 100644 --- a/sway-lib-std/src/crypto/public_key.sw +++ b/sway-lib-std/src/crypto/public_key.sw @@ -172,6 +172,7 @@ impl TryInto for PublicKey { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for PublicKey { fn eq(self, other: Self) -> bool { if self.bytes.len() != other.bytes.len() { @@ -190,6 +191,27 @@ impl core::ops::Eq for PublicKey { true } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for PublicKey { + fn eq(self, other: Self) -> bool { + if self.bytes.len() != other.bytes.len() { + return false; + } + + let mut iter = 0; + while iter < self.bytes.len() { + if self.bytes.get(iter).unwrap() != other.bytes.get(iter).unwrap() + { + return false; + } + iter += 1; + } + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for PublicKey {} impl Hash for PublicKey { fn hash(self, ref mut state: Hasher) { diff --git a/sway-lib-std/src/crypto/scalar.sw b/sway-lib-std/src/crypto/scalar.sw index 65b5f5574cf..7c76fb4c8e3 100644 --- a/sway-lib-std/src/crypto/scalar.sw +++ b/sway-lib-std/src/crypto/scalar.sw @@ -10,6 +10,7 @@ pub struct Scalar { bytes: Bytes, } +#[cfg(experimental_partial_eq = false)] impl Eq for Scalar { fn eq(self, other: Self) -> bool { // All scalars must be of length 32 @@ -29,6 +30,27 @@ impl Eq for Scalar { true } } +#[cfg(experimental_partial_eq = true)] +impl PartialEq for Scalar { + fn eq(self, other: Self) -> bool { + if self.bytes.len() != 32 || other.bytes.len() != 32 { + return false; + } + + let mut iter = 0; + while iter < 32 { + if self.bytes.get(iter).unwrap() != other.bytes.get(iter).unwrap() + { + return false; + } + + iter += 1; + } + true + } +} +#[cfg(experimental_partial_eq = true)] +impl Eq for Scalar {} impl Scalar { /// Returns a new, uninitialized Scalar. diff --git a/sway-lib-std/src/crypto/secp256k1.sw b/sway-lib-std/src/crypto/secp256k1.sw index 2249df6ecad..f555c33593b 100644 --- a/sway-lib-std/src/crypto/secp256k1.sw +++ b/sway-lib-std/src/crypto/secp256k1.sw @@ -424,6 +424,7 @@ impl Into for Secp256k1 { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Secp256k1 { fn eq(self, other: Self) -> bool { let mut iter = 0; @@ -437,6 +438,22 @@ impl core::ops::Eq for Secp256k1 { true } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Secp256k1 { + fn eq(self, other: Self) -> bool { + let mut iter = 0; + while iter < 64 { + if self.bits[iter] != other.bits[iter] { + return false; + } + iter += 1; + } + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Secp256k1 {} impl Hash for Secp256k1 { fn hash(self, ref mut state: Hasher) { diff --git a/sway-lib-std/src/crypto/secp256r1.sw b/sway-lib-std/src/crypto/secp256r1.sw index 560e8558f87..6562e9a2a19 100644 --- a/sway-lib-std/src/crypto/secp256r1.sw +++ b/sway-lib-std/src/crypto/secp256r1.sw @@ -425,6 +425,7 @@ impl Into for Secp256r1 { } } +#[cfg(experimental_partial_eq = false)] impl core::ops::Eq for Secp256r1 { fn eq(self, other: Self) -> bool { let mut iter = 0; @@ -438,6 +439,22 @@ impl core::ops::Eq for Secp256r1 { true } } +#[cfg(experimental_partial_eq = true)] +impl core::ops::PartialEq for Secp256r1 { + fn eq(self, other: Self) -> bool { + let mut iter = 0; + while iter < 64 { + if self.bits[iter] != other.bits[iter] { + return false; + } + iter += 1; + } + + true + } +} +#[cfg(experimental_partial_eq = true)] +impl core::ops::Eq for Secp256r1 {} impl Hash for Secp256r1 { fn hash(self, ref mut state: Hasher) { diff --git a/test/src/sdk-harness/test_projects/tx_fields/mod.rs b/test/src/sdk-harness/test_projects/tx_fields/mod.rs index 59c29f27442..a3d6739e7c2 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -1630,8 +1630,6 @@ mod outputs { use super::*; mod success { - use fuel_core_client::client::schema::schema::__fields::GasCosts::exp; - use super::*; #[tokio::test]