From cdd298fe37bf8fd82b11a3a380b34687a56159a2 Mon Sep 17 00:00:00 2001 From: Wolfgang Grieskamp Date: Fri, 4 Oct 2024 13:14:47 -0700 Subject: [PATCH] [move-decompiler] Model import from binary and basic decompiler setup This PR closes the loop on the needed components for the decompiler and sets up basic plumbing for the command line tool. - A major addition is a way to import Move bytecode into the move-model. Until now, this wasn't directly possible. The new module `binary_module_loader` now takes care of this. The code is mostly complete, what is missing is a way to analyze metadata to synthesize attributes. (Future PRs.) - The crate `move-decompiler` has been added, which glues the various parts which have been created in the last few PRs into a command line tool. - Some tests have been added together with the decompiler, which is also a good way to test the new `binary_module_loader`, by replicating parts of the move-stdlib. However, the decompiler isn't functional yet, the code isn't correct in multiple instances. This has to be fixed in subsequent PRs, until the tests in `move-decompiler` allow to compile and executed decompiled code. --- Cargo.lock | 21 + Cargo.toml | 2 + .../move/move-binary-format/src/views.rs | 8 + .../move-command-line-common/src/files.rs | 6 +- third_party/move/move-compiler-v2/src/lib.rs | 13 +- .../bug_14300_update_variant_select.exp | 12 +- .../bug_14300_variant_select_autoref.exp | 2 +- .../bytecode-generator/conditional_borrow.exp | 6 +- .../bytecode-generator/freeze_mut_ref.exp | 12 +- .../tests/bytecode-generator/if_else.exp | 7 +- .../tests/bytecode-generator/loop_labels.exp | 6 +- .../tests/bytecode-generator/matching_ok.exp | 4 +- .../break_outside_loop_in_else.exp | 3 +- .../v1-typing/global_invalid.exp | 3 +- .../checking/access_specifiers/access_ok.exp | 11 +- .../control_flow/loop_labels_check_ok.exp | 6 +- .../tests/checking/dotdot/dotdot_valid.exp | 3 +- .../tests/checking/indexing/examples_book.exp | 6 +- .../tests/checking/inlining/bug_11112.exp | 3 +- .../tests/checking/inlining/bug_9717.exp | 6 +- .../checking/inlining/double_nesting.exp | 3 +- .../tests/checking/inlining/lambda.exp | 3 +- .../tests/checking/inlining/lambda_typed.exp | 3 +- .../tests/checking/inlining/nested_mul.exp | 3 +- .../checking/inlining/recursive_nesting.exp | 3 +- .../checking/inlining/shadowing_unused.exp | 6 +- .../inlining/shadowing_unused_nodecl.exp | 6 +- .../shadowing_unused_nodecl_typed.exp | 6 +- .../inlining/shadowing_unused_typed.exp | 6 +- .../tests/checking/inlining/spec_inlining.exp | 6 +- .../checking/inlining/spec_inlining_typed.exp | 6 +- .../checking/inlining/temp_shadowing.exp | 3 +- .../naming/duplicate_acquires_list_item.exp | 10 +- .../positional_fields/assign_field.exp | 3 +- .../positional_fields/common_access.exp | 2 +- .../tests/checking/receiver/same_names.exp | 3 +- .../checking/receiver/same_names_typed.exp | 3 +- .../specs/assert_skipped_for_spec.exp | 3 +- .../typing/borrow_field_complex_root_expr.exp | 15 +- .../typing/borrow_field_non_ref_root.exp | 6 +- .../checking/typing/if_branches_subtype.exp | 18 +- .../checking/typing/if_matched_branches.exp | 21 +- ...t_deref_borrow_field_complex_root_expr.exp | 12 +- ...ef_borrow_field_non_ref_non_local_root.exp | 6 +- ...plicit_deref_borrow_field_non_ref_root.exp | 3 +- .../checking/typing/nested_post_process.exp | 3 +- .../tests/checking/typing/other_builtins.exp | 6 +- .../typing/return_type_explicit_exp.exp | 3 +- .../checking/typing/seq_ignores_value.exp | 3 +- .../multi_pool_money_market_token.exp | 46 +- .../v1-examples/simple_money_market_token.exp | 43 +- .../checking/variants/variants_constants.exp | 8 +- .../tests/checking/variants/variants_ok.exp | 4 +- .../constant_folding_ristretto.exp | 3 +- .../simplifier-elimination/double_nesting.exp | 3 +- .../else_assigns_if_doesnt.exp | 3 +- .../if_assigns_else_doesnt.exp | 3 +- .../if_assigns_no_else.exp | 3 +- .../recursive_nesting.exp | 3 +- .../tests/simplifier/bug_11112.exp | 3 +- .../tests/simplifier/conditional_borrow.exp | 6 +- .../simplifier/constant_folding_addresses.exp | 3 +- .../simplifier/constant_folding_ristretto.exp | 3 +- .../tests/simplifier/simplifier_test4.exp | 6 +- .../tests/conditionals.exp | 13 +- .../ast-generator-tests/tests/match.exp | 27 +- .../ast-generator-tests/tests/testsuite.rs | 1 + .../move/move-model/bytecode/src/astifier.rs | 19 +- .../src/builder/binary_module_loader.rs | 629 ++++++++++++++++ .../move/move-model/src/builder/mod.rs | 1 + third_party/move/move-model/src/lib.rs | 20 +- third_party/move/move-model/src/metadata.rs | 10 + third_party/move/move-model/src/model.rs | 246 ++++--- third_party/move/move-model/src/sourcifier.rs | 53 +- third_party/move/move-model/src/ty.rs | 69 +- .../sources/functional/unused_schema.v2_exp | 9 - .../move/tools/move-decompiler/Cargo.toml | 40 ++ .../move/tools/move-decompiler/src/lib.rs | 208 ++++++ .../move/tools/move-decompiler/src/main.rs | 16 + .../move-decompiler/tests/bit_vector.exp | 65 ++ .../move-decompiler/tests/bit_vector.move | 241 +++++++ .../tools/move-decompiler/tests/error.exp | 42 ++ .../tools/move-decompiler/tests/error.move | 88 +++ .../move-decompiler/tests/fixed_point32.exp | 94 +++ .../move-decompiler/tests/fixed_point32.move | 295 ++++++++ .../tools/move-decompiler/tests/option.exp | 119 ++++ .../tools/move-decompiler/tests/option.move | 356 ++++++++++ .../move-decompiler/tests/simple_map.exp | 160 +++++ .../move-decompiler/tests/simple_map.move | 319 +++++++++ .../tools/move-decompiler/tests/string.exp | 73 ++ .../tools/move-decompiler/tests/string.move | 93 +++ .../tools/move-decompiler/tests/testsuite.rs | 86 +++ .../tools/move-decompiler/tests/vector.exp | 176 +++++ .../tools/move-decompiler/tests/vector.move | 671 ++++++++++++++++++ 94 files changed, 4410 insertions(+), 283 deletions(-) create mode 100644 third_party/move/move-model/src/builder/binary_module_loader.rs delete mode 100644 third_party/move/move-prover/tests/sources/functional/unused_schema.v2_exp create mode 100644 third_party/move/tools/move-decompiler/Cargo.toml create mode 100644 third_party/move/tools/move-decompiler/src/lib.rs create mode 100644 third_party/move/tools/move-decompiler/src/main.rs create mode 100644 third_party/move/tools/move-decompiler/tests/bit_vector.exp create mode 100644 third_party/move/tools/move-decompiler/tests/bit_vector.move create mode 100644 third_party/move/tools/move-decompiler/tests/error.exp create mode 100644 third_party/move/tools/move-decompiler/tests/error.move create mode 100644 third_party/move/tools/move-decompiler/tests/fixed_point32.exp create mode 100644 third_party/move/tools/move-decompiler/tests/fixed_point32.move create mode 100644 third_party/move/tools/move-decompiler/tests/option.exp create mode 100644 third_party/move/tools/move-decompiler/tests/option.move create mode 100644 third_party/move/tools/move-decompiler/tests/simple_map.exp create mode 100644 third_party/move/tools/move-decompiler/tests/simple_map.move create mode 100644 third_party/move/tools/move-decompiler/tests/string.exp create mode 100644 third_party/move/tools/move-decompiler/tests/string.move create mode 100644 third_party/move/tools/move-decompiler/tests/testsuite.rs create mode 100644 third_party/move/tools/move-decompiler/tests/vector.exp create mode 100644 third_party/move/tools/move-decompiler/tests/vector.move diff --git a/Cargo.lock b/Cargo.lock index 4ab17dbdbd8d6f..176e0c5cc6d77d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11144,6 +11144,27 @@ dependencies = [ "serde", ] +[[package]] +name = "move-decompiler" +version = "0.1.0" +dependencies = [ + "anyhow", + "bcs 0.1.4", + "clap 4.4.14", + "codespan", + "codespan-reporting", + "colored", + "datatest-stable", + "move-binary-format", + "move-bytecode-source-map", + "move-command-line-common", + "move-compiler-v2", + "move-core-types", + "move-model", + "move-prover-test-utils", + "move-stackless-bytecode", +] + [[package]] name = "move-disassembler" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d1f271a95416b6..cae40dfd811527 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -244,6 +244,7 @@ members = [ "third_party/move/tools/move-bytecode-viewer", "third_party/move/tools/move-cli", "third_party/move/tools/move-coverage", + "third_party/move/tools/move-decompiler", "third_party/move/tools/move-disassembler", "third_party/move/tools/move-explain", "third_party/move/tools/move-package", @@ -825,6 +826,7 @@ move-coverage = { path = "third_party/move/tools/move-coverage" } move-compiler = { path = "third_party/move/move-compiler" } move-compiler-v2 = { path = "third_party/move/move-compiler-v2" } move-core-types = { path = "third_party/move/move-core/types" } +move-decompiler = { path = "third_party/move/tools/move-decompiler" } move-docgen = { path = "third_party/move/move-prover/move-docgen" } move-disassembler = { path = "third_party/move/tools/move-disassembler" } move-ir-types = { path = "third_party/move/move-ir/types" } diff --git a/third_party/move/move-binary-format/src/views.rs b/third_party/move/move-binary-format/src/views.rs index 06f6da840c1e7c..d2952dd646d4c7 100644 --- a/third_party/move/move-binary-format/src/views.rs +++ b/third_party/move/move-binary-format/src/views.rs @@ -349,6 +349,10 @@ impl<'a, T: ModuleAccess> StructDefinitionView<'a, T> { self.struct_handle_view.abilities() } + pub fn handle_idx(&self) -> StructHandleIndex { + self.struct_def.struct_handle + } + pub fn is_native(&self) -> bool { match &self.struct_def.field_information { StructFieldInformation::Native => true, @@ -491,6 +495,10 @@ impl<'a, T: ModuleAccess> FunctionDefinitionView<'a, T> { } } + pub fn handle_idx(&self) -> FunctionHandleIndex { + self.function_def.function + } + pub fn visibility(&self) -> Visibility { self.function_def.visibility } diff --git a/third_party/move/move-command-line-common/src/files.rs b/third_party/move/move-command-line-common/src/files.rs index e265c474f648f6..79b4e0e5c34e45 100644 --- a/third_party/move/move-command-line-common/src/files.rs +++ b/third_party/move/move-command-line-common/src/files.rs @@ -13,7 +13,11 @@ pub struct FileHash(pub [u8; 32]); impl FileHash { pub fn new(file_contents: &str) -> Self { - Self(sha2::Sha256::digest(file_contents.as_bytes()).into()) + Self::new_from_bytes(file_contents.as_bytes()) + } + + pub fn new_from_bytes(bytes: &[u8]) -> Self { + Self(sha2::Sha256::digest(bytes).into()) } pub const fn empty() -> Self { diff --git a/third_party/move/move-compiler-v2/src/lib.rs b/third_party/move/move-compiler-v2/src/lib.rs index f1c9ab6ba0394f..7f79b192c08ce2 100644 --- a/third_party/move/move-compiler-v2/src/lib.rs +++ b/third_party/move/move-compiler-v2/src/lib.rs @@ -39,7 +39,6 @@ use crate::{ variable_coalescing::VariableCoalescing, }, }; -use anyhow::bail; use codespan_reporting::{ diagnostic::Severity, term::termcolor::{ColorChoice, StandardStream, WriteColor}, @@ -596,21 +595,13 @@ fn get_vm_error_loc(env: &GlobalEnv, source_map: &SourceMap, e: &VMError) -> Opt } /// Report any diags in the env to the writer and fail if there are errors. -pub fn check_errors( - env: &GlobalEnv, - error_writer: &mut W, - msg: &'static str, -) -> anyhow::Result<()> +pub fn check_errors(env: &GlobalEnv, error_writer: &mut W, msg: &str) -> anyhow::Result<()> where W: WriteColor + Write, { let options = env.get_extension::().unwrap_or_default(); env.report_diag(error_writer, options.report_severity()); - if env.has_errors() { - bail!("exiting with {}", msg); - } else { - Ok(()) - } + env.check_diag(error_writer, options.report_severity(), msg) } /// Annotate the given compiled units. diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.exp index 453eb08801151e..e81fbf75afa73d 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.exp @@ -55,18 +55,18 @@ module 0x815::m { } fun update_common_field(): u64 { let common = CommonFields::Bar{x: 30,y: 40u8,z: 50u32}; - common.Foo.x = 15; - common.Foo.x + common.x = 15; + common.x } fun update_common_field_different_offset(): u8 { let common = CommonFields::Bar{x: 30,y: 40u8,z: 50u32}; - common.Foo.y = 15u8; - common.Foo.y + common.y = 15u8; + common.y } fun update_non_common_field(): u32 { let common = CommonFields::Bar{x: 30,y: 40u8,z: 50u32}; - common.Bar.z = 15u32; - common.Bar.z + common.z = 15u32; + common.z } } diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.exp index 0d74991c8f9b01..c26adc0db393df 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.exp @@ -29,7 +29,7 @@ module 0x815::m { } fun test_common_access(): u8 { let x = Positional::A(42u8); - x.A.0 = 19u8; + x.0 = 19u8; 20u8 } } diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/conditional_borrow.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/conditional_borrow.exp index 2e4693e518bd75..31e8d561578421 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/conditional_borrow.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/conditional_borrow.exp @@ -105,7 +105,8 @@ module 0x8675::M { test1(7) + test1(2) } fun test1(r: u64): u64 { - let tref = &mut (if (r < 4) r else 3); + let tref = &mut (if (r < 4) r + else 3); *tref = 10; let y = r; let tref2 = &mut y; @@ -127,7 +128,8 @@ module 0x8675::M { } fun test1b(r: S): u64 { let x = S{f: 3}; - let tref = &mut (if (r.f < 4) r else x); + let tref = &mut (if (r.f < 4) r + else x); (*tref).f = 10; let y = r; let tref2 = &mut y; diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/freeze_mut_ref.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/freeze_mut_ref.exp index cf24cf32909467..f17461b2de46c4 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/freeze_mut_ref.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/freeze_mut_ref.exp @@ -117,7 +117,8 @@ module 0x42::freeze_mut_ref { /*freeze*/v } public fun borrow_mut3(v1: &mut Element, v2: &Element): &Element { - if (true) /*freeze*/v1 else v2 + if (true) /*freeze*/v1 + else v2 } public fun borrow_mut4(v: &mut Element): &Element { /*freeze*/v @@ -157,14 +158,17 @@ module 0x42::freeze_mut_ref { } fun t6(cond: bool, s: &mut S, other: &S) { let x; - if (cond) x = /*freeze*/copy s else x = other; + if (cond) x = /*freeze*/copy s + else x = other; } fun t7(cond: bool, s: &mut S, other: &S) { let _x; - _x = if (cond) /*freeze*/s else other; + _x = if (cond) /*freeze*/s + else other; } fun t8(cond: bool, s: &mut S, other: &S) { - let _x = if (cond) /*freeze*/s else other; + let _x = if (cond) /*freeze*/s + else other; } } diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/if_else.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/if_else.exp index a8faa766b2a7a2..f6f577bc92736e 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/if_else.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/if_else.exp @@ -23,10 +23,13 @@ module 0x42::if_else { // -- Sourcified model before bytecode pipeline module 0x42::if_else { fun if_else(cond: bool, x: u64): u64 { - if (cond) x + 1 else x - 1 + if (cond) x + 1 + else x - 1 } fun if_else_nested(cond: bool, x: u64): u64 { - if ((if (cond) x + 1 else x - 1) > 10) x * 2 else x / 2 + if ((if (cond) x + 1 + else x - 1) > 10) x * 2 + else x / 2 } } diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/loop_labels.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/loop_labels.exp index 247f1e144dfdb4..14215885d8b79c 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/loop_labels.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/loop_labels.exp @@ -28,9 +28,11 @@ module 0x815::test { fun f1() { 'l0: loop { loop 'l1: loop if (true) loop { - if (false) continue 'l0 else break 'l1; + if (false) continue 'l0 + else break 'l1; break - } else continue 'l0; + } + else continue 'l0; break } } diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_ok.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_ok.exp index a49fde8a4b9a3c..0e40ef60561744 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_ok.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/matching_ok.exp @@ -320,13 +320,13 @@ module 0xc0ffee::m { } } fun select_common_fields(s: CommonFields): u64 { - s.Foo.x + (match (s) { + s.x + (match (s) { CommonFields::Foo{x: _,y: y} => y, CommonFields::Bar{x: _,z: z} => z, }) } fun select_common_fields_different_offset(s: CommonFieldsAtDifferentOffset): u64 { - s.Bar.z + s.z } fun test_common(s: CommonFields): bool { s is Foo | Bar diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-commands/break_outside_loop_in_else.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-commands/break_outside_loop_in_else.exp index d527f8cbda14d2..e94c3e61561c16 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-commands/break_outside_loop_in_else.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-commands/break_outside_loop_in_else.exp @@ -13,7 +13,8 @@ module _0 { // -- Sourcified model before bytecode pipeline script { fun main() { - if (false) () else break; + if (false) () + else break; } } diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-typing/global_invalid.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-typing/global_invalid.exp index 34f06097556ec7..a9ea7404d770fb 100644 --- a/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-typing/global_invalid.exp +++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/v1-typing/global_invalid.exp @@ -17,7 +17,8 @@ module 0x42::m { // -- Sourcified model before bytecode pipeline module 0x42::m { fun invalid(addr: address) { - if (exists(addr)) () else abort 0; + if (exists(addr)) () + else abort 0; let _ = borrow_global(addr); move_from(addr); } diff --git a/third_party/move/move-compiler-v2/tests/checking/access_specifiers/access_ok.exp b/third_party/move/move-compiler-v2/tests/checking/access_specifiers/access_ok.exp index 8298da9baae101..2c290860107c83 100644 --- a/third_party/move/move-compiler-v2/tests/checking/access_specifiers/access_ok.exp +++ b/third_party/move/move-compiler-v2/tests/checking/access_specifiers/access_ok.exp @@ -122,17 +122,14 @@ module 0x42::m { } fun f10(x: u64) acquires *(m::make_up_address(x)) - { } fun f11() !reads *(0x42) !reads *(0x43) - { } fun f12() - { } fun f2() @@ -161,16 +158,18 @@ module 0x42::m { } fun f8() acquires *(0x42) - { } fun f9(a: address) acquires *(a) - { } fun f_multiple() - acquires Rreads Rwrites Twrites Sreads G + acquires R + reads R + writes T + writes S + reads G { } fun make_up_address(x: u64): address { diff --git a/third_party/move/move-compiler-v2/tests/checking/control_flow/loop_labels_check_ok.exp b/third_party/move/move-compiler-v2/tests/checking/control_flow/loop_labels_check_ok.exp index 3bf1a2816123fc..b0f82af3a7e1f0 100644 --- a/third_party/move/move-compiler-v2/tests/checking/control_flow/loop_labels_check_ok.exp +++ b/third_party/move/move-compiler-v2/tests/checking/control_flow/loop_labels_check_ok.exp @@ -28,9 +28,11 @@ module 0x815::test { fun f1() { 'l0: loop { loop 'l1: loop if (true) loop { - if (false) continue 'l0 else break 'l1; + if (false) continue 'l0 + else break 'l1; break - } else continue 'l0; + } + else continue 'l0; break } } diff --git a/third_party/move/move-compiler-v2/tests/checking/dotdot/dotdot_valid.exp b/third_party/move/move-compiler-v2/tests/checking/dotdot/dotdot_valid.exp index 6b25517efe372b..fcfe9b97fcec54 100644 --- a/third_party/move/move-compiler-v2/tests/checking/dotdot/dotdot_valid.exp +++ b/third_party/move/move-compiler-v2/tests/checking/dotdot/dotdot_valid.exp @@ -443,7 +443,8 @@ module 0x42::test { } fun simple_5(x: E1): u8 { match (x) { - E1::A(_,y) => if (y) 1u8 else 0u8, + E1::A(_,y) => if (y) 1u8 + else 0u8, E1::B(x) => x, E1::C{x: _,y: S1(x)} => x, } diff --git a/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.exp b/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.exp index 979ec749c6216b..4d8b0323a5a687 100644 --- a/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.exp +++ b/third_party/move/move-compiler-v2/tests/checking/indexing/examples_book.exp @@ -35,8 +35,10 @@ module 0x1::m { { let x = borrow_global_mut(0x1); x.value = false; - if (borrow_global(0x1).value == false) () else abort 1; + if (borrow_global(0x1).value == false) () + else abort 1; borrow_global_mut(0x1).value = true; - if (borrow_global(0x1).value == true) () else abort 2; + if (borrow_global(0x1).value == true) () + else abort 2; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/bug_11112.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/bug_11112.exp index fb79d81744afaf..655dda4ff1b7c0 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/bug_11112.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/bug_11112.exp @@ -52,7 +52,8 @@ module 0xcafe::vectors { i = i + 1 } }; - if (v == vector[2, 3, 4]) () else abort 0; + if (v == vector[2, 3, 4]) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/bug_9717.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/bug_9717.exp index 5462c607e72ad6..a4d0a3c8e2361d 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/bug_9717.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/bug_9717.exp @@ -202,7 +202,8 @@ module 0xcafe::vectors { let (flips) = (&flips); let i = 0; while (i < 0x1::vector::length(flips)) { - if (*0x1::vector::borrow(flips, i) == 0u8) () else abort 3; + if (*0x1::vector::borrow(flips, i) == 0u8) () + else abort 3; i = i + 1; }; }; @@ -226,7 +227,8 @@ module 0xcafe::vectors { inline fun loops_without_break(flips: &vector) { let i = 0; while (i < 0x1::vector::length(flips)) { - if (*0x1::vector::borrow(flips, i) == 0u8) () else abort 3; + if (*0x1::vector::borrow(flips, i) == 0u8) () + else abort 3; i = i + 1; }; } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/double_nesting.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/double_nesting.exp index 013e348b7560af..4d0f047e9b8bc1 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/double_nesting.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/double_nesting.exp @@ -37,7 +37,8 @@ module 0x42::test { use 0x42::mathtest2; use 0x42::mathtest; fun test_nested_fun1() { - if (true) () else abort 0; + if (true) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/lambda.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/lambda.exp index b9080d09fb5459..873c8ef63fe4b7 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/lambda.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/lambda.exp @@ -159,7 +159,8 @@ module 0x42::LambdaTest { 1120 } fun test_lambda() { - if (false) () else abort 0; + if (false) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/lambda_typed.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/lambda_typed.exp index b9080d09fb5459..873c8ef63fe4b7 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/lambda_typed.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/lambda_typed.exp @@ -159,7 +159,8 @@ module 0x42::LambdaTest { 1120 } fun test_lambda() { - if (false) () else abort 0; + if (false) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/nested_mul.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/nested_mul.exp index fd17730d179b6e..268d76f0c2cc65 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/nested_mul.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/nested_mul.exp @@ -25,7 +25,8 @@ module 0x42::mathtest { module 0x42::test { use 0x42::mathtest; fun test_nested_mul_div() { - if (true) () else abort 0; + if (true) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/recursive_nesting.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/recursive_nesting.exp index be3551312c2b45..b3ee16f0486d9d 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/recursive_nesting.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/recursive_nesting.exp @@ -65,7 +65,8 @@ module 0x42::test { use 0x42::mathtest2; use 0x42::mathtest3; fun test_nested_mul_div() { - if (true) () else abort 0; + if (true) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused.exp index 5eb27791a58271..5302aee9de8ad4 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused.exp @@ -57,14 +57,16 @@ module 0x42::Test { _x = 3; }; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } public fun test_shadowing2() { let _x = 1; { _x = 3; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl.exp index 96b8d2c461add7..711b0f5e4f17fa 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl.exp @@ -65,14 +65,16 @@ module 0x42::Test { _x = 3; }; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } public fun test_shadowing2() { let _x = 1; { _x = 3; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl_typed.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl_typed.exp index 9dc677d49a25e5..8d0774b535ad4e 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl_typed.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_nodecl_typed.exp @@ -65,14 +65,16 @@ module 0x42::Test { _x = 3; }; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } public fun test_shadowing2() { let _x = 1; { _x = 3; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_typed.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_typed.exp index 5eb27791a58271..5302aee9de8ad4 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_typed.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/shadowing_unused_typed.exp @@ -57,14 +57,16 @@ module 0x42::Test { _x = 3; }; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } public fun test_shadowing2() { let _x = 1; { _x = 3; }; - if (_x == 3) () else abort 0 + if (_x == 3) () + else abort 0 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining.exp index c63cdd3aaa4355..ff900a6b4e8785 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining.exp @@ -85,7 +85,8 @@ module 0x42::Test { } */ ; - if (r1) () else abort 1; + if (r1) () + else abort 1; let r2 = { let (v) = (x); @@ -103,7 +104,8 @@ module 0x42::Test { } */ ; - if (r2) () else abort 2; + if (r2) () + else abort 2; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining_typed.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining_typed.exp index c63cdd3aaa4355..ff900a6b4e8785 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining_typed.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/spec_inlining_typed.exp @@ -85,7 +85,8 @@ module 0x42::Test { } */ ; - if (r1) () else abort 1; + if (r1) () + else abort 1; let r2 = { let (v) = (x); @@ -103,7 +104,8 @@ module 0x42::Test { } */ ; - if (r2) () else abort 2; + if (r2) () + else abort 2; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/inlining/temp_shadowing.exp b/third_party/move/move-compiler-v2/tests/checking/inlining/temp_shadowing.exp index 21b28b8d8ae5c5..82804a35b77d09 100644 --- a/third_party/move/move-compiler-v2/tests/checking/inlining/temp_shadowing.exp +++ b/third_party/move/move-compiler-v2/tests/checking/inlining/temp_shadowing.exp @@ -85,7 +85,8 @@ module 0x42::Test { } public fun test_shadowing() { let z = other(1, 4); - if (z == 10) () else abort z + if (z == 10) () + else abort z } } diff --git a/third_party/move/move-compiler-v2/tests/checking/naming/duplicate_acquires_list_item.exp b/third_party/move/move-compiler-v2/tests/checking/naming/duplicate_acquires_list_item.exp index 3a1b5c04d51a49..018644c2d57d9c 100644 --- a/third_party/move/move-compiler-v2/tests/checking/naming/duplicate_acquires_list_item.exp +++ b/third_party/move/move-compiler-v2/tests/checking/naming/duplicate_acquires_list_item.exp @@ -35,13 +35,19 @@ module 0x8675309::M { struct X has key { } fun t0() - acquires Racquires Xacquires R + acquires R + acquires X + acquires R { borrow_global_mut(0x1); borrow_global_mut(0x1); } fun t1() - acquires Racquires Xacquires Racquires Racquires R + acquires R + acquires X + acquires R + acquires R + acquires R { borrow_global_mut(0x1); borrow_global_mut(0x1); diff --git a/third_party/move/move-compiler-v2/tests/checking/positional_fields/assign_field.exp b/third_party/move/move-compiler-v2/tests/checking/positional_fields/assign_field.exp index c84034b0a95a82..162a3e44abdfe0 100644 --- a/third_party/move/move-compiler-v2/tests/checking/positional_fields/assign_field.exp +++ b/third_party/move/move-compiler-v2/tests/checking/positional_fields/assign_field.exp @@ -131,7 +131,8 @@ module 0x42::test { fun assign1(x: S1): u64 { let count = 0; while (x.1) { - let y = if (x.0 > 0) x.0 - 1 else 0; + let y = if (x.0 > 0) x.0 - 1 + else 0; x = S1(y,y >= 1); count = count + 1; }; diff --git a/third_party/move/move-compiler-v2/tests/checking/positional_fields/common_access.exp b/third_party/move/move-compiler-v2/tests/checking/positional_fields/common_access.exp index 38c21b26528576..a8d7709da2113b 100644 --- a/third_party/move/move-compiler-v2/tests/checking/positional_fields/common_access.exp +++ b/third_party/move/move-compiler-v2/tests/checking/positional_fields/common_access.exp @@ -24,6 +24,6 @@ module 0x42::test { } } fun common_access(x: Foo): u8 { - x.A.0 + x.0 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/receiver/same_names.exp b/third_party/move/move-compiler-v2/tests/checking/receiver/same_names.exp index 9f205590de554b..fa0674703b7178 100644 --- a/third_party/move/move-compiler-v2/tests/checking/receiver/same_names.exp +++ b/third_party/move/move-compiler-v2/tests/checking/receiver/same_names.exp @@ -62,6 +62,7 @@ module 0x42::c { fun test(x: a::MyList, y: b::MyOtherList) { let (x,y) = (x, y); let (x,y) = (x, y); - if (a::len(&x) + b::len(&y) == 1) () else abort 1 + if (a::len(&x) + b::len(&y) == 1) () + else abort 1 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/receiver/same_names_typed.exp b/third_party/move/move-compiler-v2/tests/checking/receiver/same_names_typed.exp index 9f205590de554b..fa0674703b7178 100644 --- a/third_party/move/move-compiler-v2/tests/checking/receiver/same_names_typed.exp +++ b/third_party/move/move-compiler-v2/tests/checking/receiver/same_names_typed.exp @@ -62,6 +62,7 @@ module 0x42::c { fun test(x: a::MyList, y: b::MyOtherList) { let (x,y) = (x, y); let (x,y) = (x, y); - if (a::len(&x) + b::len(&y) == 1) () else abort 1 + if (a::len(&x) + b::len(&y) == 1) () + else abort 1 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/specs/assert_skipped_for_spec.exp b/third_party/move/move-compiler-v2/tests/checking/specs/assert_skipped_for_spec.exp index 6c0bb9bfdcd4d1..dc5c10f9266ddb 100644 --- a/third_party/move/move-compiler-v2/tests/checking/specs/assert_skipped_for_spec.exp +++ b/third_party/move/move-compiler-v2/tests/checking/specs/assert_skipped_for_spec.exp @@ -19,7 +19,8 @@ module 0x42::M { // -- Sourcified model before bytecode pipeline module 0x42::M { fun bar(x: u64): u64 { - if (x > 0) () else abort 1; + if (x > 0) () + else abort 1; x - 1 } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_complex_root_expr.exp b/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_complex_root_expr.exp index 8effc39a97ef24..05e4d7edb8acd8 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_complex_root_expr.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_complex_root_expr.exp @@ -43,11 +43,16 @@ module 0x8675309::M { f: u64, } fun t0(cond: bool, s: &S, s_mut: &mut S) { - &(if (cond) s else s).f; - &(if (cond) /*freeze*/s_mut else s).f; - &(if (cond) s else /*freeze*/s_mut).f; - &(if (cond) s_mut else s_mut).f; - &mut (if (cond) s_mut else s_mut).f; + &(if (cond) s + else s).f; + &(if (cond) /*freeze*/s_mut + else s).f; + &(if (cond) s + else /*freeze*/s_mut).f; + &(if (cond) s_mut + else s_mut).f; + &mut (if (cond) s_mut + else s_mut).f; &{ let s = S{f: 0}; &s diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_non_ref_root.exp b/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_non_ref_root.exp index d3eaf882a24453..70a9360f566688 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_non_ref_root.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/borrow_field_non_ref_root.exp @@ -28,7 +28,9 @@ module 0x8675309::M { fun t0(cond: bool, s: S) { &s.f; &mut s.f; - &(if (cond) S{f: 0} else S{f: 1}).f; - &mut (if (cond) S{f: 0} else S{f: 1}).f; + &(if (cond) S{f: 0} + else S{f: 1}).f; + &mut (if (cond) S{f: 0} + else S{f: 1}).f; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/if_branches_subtype.exp b/third_party/move/move-compiler-v2/tests/checking/typing/if_branches_subtype.exp index 4efc4b81f3766f..8ec4cd2e676a66 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/if_branches_subtype.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/if_branches_subtype.exp @@ -53,13 +53,19 @@ module 0x8675309::M { // -- Sourcified model before bytecode pipeline module 0x8675309::M { fun t0(cond: bool, u: &u64, u_mut: &mut u64) { - let _ = if (cond) u else /*freeze*/u_mut; - let _ = if (cond) /*freeze*/u_mut else u; - let _ = if (cond) /*freeze*/u_mut else /*freeze*/u_mut; + let _ = if (cond) u + else /*freeze*/u_mut; + let _ = if (cond) /*freeze*/u_mut + else u; + let _ = if (cond) /*freeze*/u_mut + else /*freeze*/u_mut; } fun t1(cond: bool, u: &u64, u_mut: &mut u64) { - let (_,_) = if (cond) (u, u) else (/*freeze*/u_mut, /*freeze*/u_mut); - let (_,_) = if (cond) (/*freeze*/u_mut, u) else (u, /*freeze*/u_mut); - let (_,_) = if (cond) (u, /*freeze*/u_mut) else (/*freeze*/u_mut, u); + let (_,_) = if (cond) (u, u) + else (/*freeze*/u_mut, /*freeze*/u_mut); + let (_,_) = if (cond) (/*freeze*/u_mut, u) + else (u, /*freeze*/u_mut); + let (_,_) = if (cond) (u, /*freeze*/u_mut) + else (/*freeze*/u_mut, u); } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/if_matched_branches.exp b/third_party/move/move-compiler-v2/tests/checking/typing/if_matched_branches.exp index 1ddad9454c1b92..374e43df422b79 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/if_matched_branches.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/if_matched_branches.exp @@ -62,14 +62,21 @@ module 0x8675309::M { if (cond) (); } fun t1(cond: bool) { - if (cond) 0 else 0; - if (cond) false else false; - R{} = if (cond) R{} else R{}; - if (cond) &0 else &1; - if (cond) &mut 0 else &mut 1; + if (cond) 0 + else 0; + if (cond) false + else false; + R{} = if (cond) R{} + else R{}; + if (cond) &0 + else &1; + if (cond) &mut 0 + else &mut 1; } fun t2(cond: bool) { - if (cond) (0, false) else (1, true); - (_,_,_,R{}) = if (cond) (0, 0, &0, R{}) else (1, 1, &1, R{}); + if (cond) (0, false) + else (1, true); + (_,_,_,R{}) = if (cond) (0, 0, &0, R{}) + else (1, 1, &1, R{}); } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_complex_root_expr.exp b/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_complex_root_expr.exp index f3d40cef51c378..b603f633bc7c09 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_complex_root_expr.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_complex_root_expr.exp @@ -38,10 +38,14 @@ module 0x8675309::M { f: u64, } fun t0(cond: bool, s: &S, s_mut: &mut S) { - (if (cond) s else s).f; - (if (cond) /*freeze*/s_mut else s).f; - (if (cond) s else /*freeze*/s_mut).f; - (if (cond) s_mut else s_mut).f; + (if (cond) s + else s).f; + (if (cond) /*freeze*/s_mut + else s).f; + (if (cond) s + else /*freeze*/s_mut).f; + (if (cond) s_mut + else s_mut).f; { let s = S{f: 0}; &s diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_non_local_root.exp b/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_non_local_root.exp index f94c9cb4a3e059..a6c40f92df4d26 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_non_local_root.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_non_local_root.exp @@ -40,7 +40,9 @@ module 0x8675309::M { fun t0(cond: bool, _s: S) { foo().f; bar().f; - (if (cond) foo() else &bar()).f; - (if (cond) *foo() else bar()).f; + (if (cond) foo() + else &bar()).f; + (if (cond) *foo() + else bar()).f; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_root.exp b/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_root.exp index 730ca3e3600dac..bf8e3055743598 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_root.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/implicit_deref_borrow_field_non_ref_root.exp @@ -21,6 +21,7 @@ module 0x8675309::M { } fun t0(cond: bool, s: S) { s.f; - (if (cond) S{f: 0} else S{f: 1}).f; + (if (cond) S{f: 0} + else S{f: 1}).f; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/nested_post_process.exp b/third_party/move/move-compiler-v2/tests/checking/typing/nested_post_process.exp index 302baa8097a2f9..63618d2d968d90 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/nested_post_process.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/nested_post_process.exp @@ -62,7 +62,8 @@ module 0x42::simple_map { } public fun borrow(map: &SimpleMap, key: &Key): &Value { let maybe_idx = find(map, key); - if (0x1::option::is_some(&maybe_idx)) () else abort 0x1::error::invalid_argument(2); + if (0x1::option::is_some(&maybe_idx)) () + else abort 0x1::error::invalid_argument(2); let idx = 0x1::option::extract(&mut maybe_idx); &0x1::vector::borrow>(&map.data, idx).value } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/other_builtins.exp b/third_party/move/move-compiler-v2/tests/checking/typing/other_builtins.exp index c9bac663000b31..8553e9ff628160 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/other_builtins.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/other_builtins.exp @@ -28,7 +28,9 @@ module 0x8675309::M { fun foo(x: &mut u64) { /*freeze*/x; /*freeze*/&mut any>(); - if (false) () else abort *x; - if (true) () else abort 0; + if (false) () + else abort *x; + if (true) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/return_type_explicit_exp.exp b/third_party/move/move-compiler-v2/tests/checking/typing/return_type_explicit_exp.exp index 71676a3601e394..0d3e8363242532 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/return_type_explicit_exp.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/return_type_explicit_exp.exp @@ -30,7 +30,8 @@ module 0x8675309::M { struct R { } fun t0() { - if (true) () else () + if (true) () + else () } fun t1(): u64 { 0 diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/seq_ignores_value.exp b/third_party/move/move-compiler-v2/tests/checking/typing/seq_ignores_value.exp index ccb4861ab2f240..8feb5e168796bc 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/seq_ignores_value.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/seq_ignores_value.exp @@ -39,6 +39,7 @@ module 0x8675309::M { (0, false, S{}); } fun t3() { - if (true) (0, false, S{}) else (0, false, S{}); + if (true) (0, false, S{}) + else (0, false, S{}); } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/multi_pool_money_market_token.exp b/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/multi_pool_money_market_token.exp index 438c492d624f69..eb99d4999a9f95 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/multi_pool_money_market_token.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/multi_pool_money_market_token.exp @@ -313,12 +313,14 @@ module 0x2::Token { } public fun deposit(coin: &mut Coin, check: Coin) { let Coin{type: type,value: value} = check; - if (&coin.type == &type) () else abort 42; + if (&coin.type == &type) () + else abort 42; coin.value = coin.value + value; } public fun destroy_zero(coin: Coin) { let Coin{type: _,value: value} = coin; - if (value == 0) () else abort 11 + if (value == 0) () + else abort 11 } public fun join(xus: Coin, coin2: Coin): Coin { deposit(&mut xus, coin2); @@ -329,7 +331,8 @@ module 0x2::Token { (coin, other) } public fun withdraw(coin: &mut Coin, amount: u64): Coin { - if (coin.value >= amount) () else abort 10; + if (coin.value >= amount) () + else abort 10; coin.value = coin.value - amount; Coin{type: *&coin.type,value: amount} } @@ -360,15 +363,20 @@ module 0x3::OneToOneMarket { price: u64, } public fun borrow(account: &signer, pool_owner: address, amount: u64): Token::Coin - acquires Priceacquires Poolacquires DepositRecordacquires BorrowRecord + acquires Price + acquires Pool + acquires DepositRecord + acquires BorrowRecord { - if (amount <= max_borrow_amount(account, pool_owner)) () else abort 1025; + if (amount <= max_borrow_amount(account, pool_owner)) () + else abort 1025; update_borrow_record(account, pool_owner, amount); let pool = borrow_global_mut>(pool_owner); Token::withdraw(&mut pool.coin, amount) } public fun deposit(account: &signer, pool_owner: address, coin: Token::Coin) - acquires Poolacquires DepositRecord + acquires Pool + acquires DepositRecord { let amount = Token::value(&coin); update_deposit_record(account, pool_owner, amount); @@ -377,7 +385,8 @@ module 0x3::OneToOneMarket { } fun accept(account: &signer, init: Token::Coin) { let sender = 0x1::signer::address_of(account); - if (!exists>(sender)) () else abort 42; + if (!exists>(sender)) () + else abort 42; move_to>(account, Pool{coin: init}) } fun borrowed_amount(account: &signer, pool_owner: address): u64 @@ -386,7 +395,8 @@ module 0x3::OneToOneMarket { let sender = 0x1::signer::address_of(account); if (!exists>(sender)) return 0; let record = &borrow_global>(sender).record; - if (Map::contains_key(record, &pool_owner)) *Map::get(record, &pool_owner) else 0 + if (Map::contains_key(record, &pool_owner)) *Map::get(record, &pool_owner) + else 0 } fun deposited_amount(account: &signer, pool_owner: address): u64 acquires DepositRecord @@ -394,20 +404,26 @@ module 0x3::OneToOneMarket { let sender = 0x1::signer::address_of(account); if (!exists>(sender)) return 0; let record = &borrow_global>(sender).record; - if (Map::contains_key(record, &pool_owner)) *Map::get(record, &pool_owner) else 0 + if (Map::contains_key(record, &pool_owner)) *Map::get(record, &pool_owner) + else 0 } fun max_borrow_amount(account: &signer, pool_owner: address): u64 - acquires Priceacquires Poolacquires DepositRecordacquires BorrowRecord + acquires Price + acquires Pool + acquires DepositRecord + acquires BorrowRecord { let input_deposited = deposited_amount(account, pool_owner); let output_deposited = borrowed_amount(account, pool_owner); let input_into_output = input_deposited * borrow_global>(pool_owner).price; - let max_output = if (input_into_output < output_deposited) 0 else input_into_output - output_deposited; + let max_output = if (input_into_output < output_deposited) 0 + else input_into_output - output_deposited; let available_output = { let pool = borrow_global>(pool_owner); Token::value(&pool.coin) }; - if (max_output < available_output) max_output else available_output + if (max_output < available_output) max_output + else available_output } public fun register_price(account: &signer, initial_in: Token::Coin, initial_out: Token::Coin, price: u64) { accept(account, initial_in); @@ -447,7 +463,8 @@ module 0x70dd::ToddNickels { nickels: Token::Coin, } public fun init(account: &signer) { - if (0x1::signer::address_of(account) == 0x70dd) () else abort 42; + if (0x1::signer::address_of(account) == 0x70dd) () + else abort 42; move_to(account, Wallet{nickels: Token::create(T{}, 0)}) } public fun destroy(c: Token::Coin) @@ -456,7 +473,8 @@ module 0x70dd::ToddNickels { Token::deposit(&mut borrow_global_mut(0x70dd).nickels, c) } public fun mint(account: &signer): Token::Coin { - if (0x1::signer::address_of(account) == 0x70dd) () else abort 42; + if (0x1::signer::address_of(account) == 0x70dd) () + else abort 42; Token::create(T{}, 5) } } diff --git a/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/simple_money_market_token.exp b/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/simple_money_market_token.exp index 3749e13a8d8199..572db7781709ed 100644 --- a/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/simple_money_market_token.exp +++ b/third_party/move/move-compiler-v2/tests/checking/typing/v1-examples/simple_money_market_token.exp @@ -260,12 +260,14 @@ module 0x2::Token { } public fun deposit(coin: &mut Coin, check: Coin) { let Coin{type: type,value: value} = check; - if (&coin.type == &type) () else abort 42; + if (&coin.type == &type) () + else abort 42; coin.value = coin.value + value; } public fun destroy_zero(coin: Coin) { let Coin{type: _,value: value} = coin; - if (value == 0) () else abort 11 + if (value == 0) () + else abort 11 } public fun join(xus: Coin, coin2: Coin): Coin { deposit(&mut xus, coin2); @@ -276,7 +278,8 @@ module 0x2::Token { (coin, other) } public fun withdraw(coin: &mut Coin, amount: u64): Coin { - if (coin.value >= amount) () else abort 10; + if (coin.value >= amount) () + else abort 10; coin.value = coin.value - amount; Coin{type: *&coin.type,value: amount} } @@ -289,7 +292,8 @@ module 0x70dd::ToddNickels { nickels: Token::Coin, } public fun init(account: &signer) { - if (0x1::signer::address_of(account) == 0x70dd) () else abort 42; + if (0x1::signer::address_of(account) == 0x70dd) () + else abort 42; move_to(account, Wallet{nickels: Token::create(T{}, 0)}) } public fun destroy(c: Token::Coin) @@ -298,7 +302,8 @@ module 0x70dd::ToddNickels { Token::deposit(&mut borrow_global_mut(0x70dd).nickels, c) } public fun mint(account: &signer): Token::Coin { - if (0x1::signer::address_of(account) == 0x70dd) () else abort 42; + if (0x1::signer::address_of(account) == 0x70dd) () + else abort 42; Token::create(T{}, 5) } } @@ -317,15 +322,20 @@ module 0xb055::OneToOneMarket { price: u64, } public fun borrow(account: &signer, amount: u64): Token::Coin - acquires Priceacquires Poolacquires DepositRecordacquires BorrowRecord + acquires Price + acquires Pool + acquires DepositRecord + acquires BorrowRecord { - if (amount <= max_borrow_amount(account)) () else abort 1025; + if (amount <= max_borrow_amount(account)) () + else abort 1025; update_borrow_record(account, amount); let pool = borrow_global_mut>(0xb055); Token::withdraw(&mut pool.coin, amount) } public fun deposit(account: &signer, coin: Token::Coin) - acquires Poolacquires DepositRecord + acquires Pool + acquires DepositRecord { let amount = Token::value(&coin); update_deposit_record(account, amount); @@ -334,7 +344,8 @@ module 0xb055::OneToOneMarket { } fun accept(account: &signer, init: Token::Coin) { let sender = 0x1::signer::address_of(account); - if (!exists>(sender)) () else abort 42; + if (!exists>(sender)) () + else abort 42; move_to>(account, Pool{coin: init}) } fun borrowed_amount(account: &signer): u64 @@ -352,21 +363,27 @@ module 0xb055::OneToOneMarket { borrow_global>(sender).record } fun max_borrow_amount(account: &signer): u64 - acquires Priceacquires Poolacquires DepositRecordacquires BorrowRecord + acquires Price + acquires Pool + acquires DepositRecord + acquires BorrowRecord { let input_deposited = deposited_amount(account); let output_deposited = borrowed_amount(account); let input_into_output = input_deposited * borrow_global>(0xb055).price; - let max_output = if (input_into_output < output_deposited) 0 else input_into_output - output_deposited; + let max_output = if (input_into_output < output_deposited) 0 + else input_into_output - output_deposited; let available_output = { let pool = borrow_global>(0xb055); Token::value(&pool.coin) }; - if (max_output < available_output) max_output else available_output + if (max_output < available_output) max_output + else available_output } public fun register_price(account: &signer, initial_in: Token::Coin, initial_out: Token::Coin, price: u64) { let sender = 0x1::signer::address_of(account); - if (sender == 0xb055) () else abort 42; + if (sender == 0xb055) () + else abort 42; accept(account, initial_in); accept(account, initial_out); move_to>(account, Price{price: price}) diff --git a/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.exp b/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.exp index a58db1808bd05f..759d1ac14df755 100644 --- a/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.exp +++ b/third_party/move/move-compiler-v2/tests/checking/variants/variants_constants.exp @@ -59,19 +59,19 @@ module 0x815::m { } fun t0(): bool { let c = Color::Red{}; - c.RGB.red == 1 + c.red == 1 } fun t1(): bool { let c = Color::Red{}; - c.RGB.red == 1 + c.red == 1 } fun t2(): bool { let c = Color::Blue{}; - c.RGB.red == 1 + c.red == 1 } fun t3(): bool { let c = Color::Blue{}; - c.RGB.red == 1 + c.red == 1 } fun t4(c: &Color) { match (c) { diff --git a/third_party/move/move-compiler-v2/tests/checking/variants/variants_ok.exp b/third_party/move/move-compiler-v2/tests/checking/variants/variants_ok.exp index 3327aba9b16e97..13f758c40d69c7 100644 --- a/third_party/move/move-compiler-v2/tests/checking/variants/variants_ok.exp +++ b/third_party/move/move-compiler-v2/tests/checking/variants/variants_ok.exp @@ -331,9 +331,9 @@ module 0x815::m { } } fun t9_common_field(self: CommonFields): u64 { - self.Foo.x + self.x } fun t9_common_field_ref(self: &CommonFields): &u64 { - &self.Foo.x + &self.x } } diff --git a/third_party/move/move-compiler-v2/tests/no-simplifier/constant_folding_ristretto.exp b/third_party/move/move-compiler-v2/tests/no-simplifier/constant_folding_ristretto.exp index 8a40a0cf6ddc32..efa4f2f99dece8 100644 --- a/third_party/move/move-compiler-v2/tests/no-simplifier/constant_folding_ristretto.exp +++ b/third_party/move/move-compiler-v2/tests/no-simplifier/constant_folding_ristretto.exp @@ -21,7 +21,8 @@ module 0xcafe::Ristretto { public fun test() { let non_canonical_highbit = vector[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8]; let non_canonical_highbit_hex = vector[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8]; - if (non_canonical_highbit == non_canonical_highbit_hex) () else abort 1; + if (non_canonical_highbit == non_canonical_highbit_hex) () + else abort 1; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier-elimination/double_nesting.exp b/third_party/move/move-compiler-v2/tests/simplifier-elimination/double_nesting.exp index 013e348b7560af..4d0f047e9b8bc1 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier-elimination/double_nesting.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier-elimination/double_nesting.exp @@ -37,7 +37,8 @@ module 0x42::test { use 0x42::mathtest2; use 0x42::mathtest; fun test_nested_fun1() { - if (true) () else abort 0; + if (true) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier-elimination/else_assigns_if_doesnt.exp b/third_party/move/move-compiler-v2/tests/simplifier-elimination/else_assigns_if_doesnt.exp index dc7f6f24d283dc..bf304cad0bc9ef 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier-elimination/else_assigns_if_doesnt.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier-elimination/else_assigns_if_doesnt.exp @@ -35,7 +35,8 @@ script { x = 42; x; }; - if (y == 0) () else abort 42; + if (y == 0) () + else abort 42; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_else_doesnt.exp b/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_else_doesnt.exp index 5c7d031f7cbe22..937f1572379258 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_else_doesnt.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_else_doesnt.exp @@ -35,7 +35,8 @@ script { y = 0; y; }; - if (x == 42) () else abort 42; + if (x == 42) () + else abort 42; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_no_else.exp b/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_no_else.exp index 7d45092d77f721..fab63b55e057b2 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_no_else.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier-elimination/if_assigns_no_else.exp @@ -30,7 +30,8 @@ script { fun main() { let x; x = 42; - if (x == 42) () else abort 42; + if (x == 42) () + else abort 42; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier-elimination/recursive_nesting.exp b/third_party/move/move-compiler-v2/tests/simplifier-elimination/recursive_nesting.exp index be3551312c2b45..b3ee16f0486d9d 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier-elimination/recursive_nesting.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier-elimination/recursive_nesting.exp @@ -65,7 +65,8 @@ module 0x42::test { use 0x42::mathtest2; use 0x42::mathtest3; fun test_nested_mul_div() { - if (true) () else abort 0; + if (true) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier/bug_11112.exp b/third_party/move/move-compiler-v2/tests/simplifier/bug_11112.exp index fb79d81744afaf..655dda4ff1b7c0 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier/bug_11112.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier/bug_11112.exp @@ -52,7 +52,8 @@ module 0xcafe::vectors { i = i + 1 } }; - if (v == vector[2, 3, 4]) () else abort 0; + if (v == vector[2, 3, 4]) () + else abort 0; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier/conditional_borrow.exp b/third_party/move/move-compiler-v2/tests/simplifier/conditional_borrow.exp index eb1d17caa267ee..3cfbdd0428bb6c 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier/conditional_borrow.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier/conditional_borrow.exp @@ -105,7 +105,8 @@ module 0x8675::M { test1(7) + test1(2) } fun test1(r: u64): u64 { - let tref = &mut (if (r < 4) r else 3); + let tref = &mut (if (r < 4) r + else 3); *tref = 10; let y = r; let tref2 = &mut y; @@ -127,7 +128,8 @@ module 0x8675::M { } fun test1b(r: S): u64 { let x = S{f: 3}; - let tref = &mut (if (r.f < 4) r else x); + let tref = &mut (if (r.f < 4) r + else x); (*tref).f = 10; let y = r; let tref2 = &mut y; diff --git a/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_addresses.exp b/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_addresses.exp index e8ce9561931f2d..2e52a07eb499d5 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_addresses.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_addresses.exp @@ -14,7 +14,8 @@ module 0xcafe::Addresses { // -- Sourcified model before bytecode pipeline module 0xcafe::Addresses { public fun test() { - if (0x1::vector::length
(&vector[0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234]) == 1845) () else abort 1; + if (0x1::vector::length
(&vector[0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234, 0x1234]) == 1845) () + else abort 1; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_ristretto.exp b/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_ristretto.exp index dfe310b087e214..7961219dc95774 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_ristretto.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier/constant_folding_ristretto.exp @@ -13,7 +13,8 @@ module 0xcafe::Ristretto { // -- Sourcified model before bytecode pipeline module 0xcafe::Ristretto { public fun test() { - if (true) () else abort 1; + if (true) () + else abort 1; } } diff --git a/third_party/move/move-compiler-v2/tests/simplifier/simplifier_test4.exp b/third_party/move/move-compiler-v2/tests/simplifier/simplifier_test4.exp index 8c31feee561260..c840b46f689b90 100644 --- a/third_party/move/move-compiler-v2/tests/simplifier/simplifier_test4.exp +++ b/third_party/move/move-compiler-v2/tests/simplifier/simplifier_test4.exp @@ -68,8 +68,10 @@ module 0x8675309::M { } = 0; 1 }) - } else x = x + 1; - if (x == 4) () else abort 0; + } + else x = x + 1; + if (x == 4) () + else abort 0; } fun t1(s: &mut S) { bar(&mut s.f, { diff --git a/third_party/move/move-model/bytecode/ast-generator-tests/tests/conditionals.exp b/third_party/move/move-model/bytecode/ast-generator-tests/tests/conditionals.exp index faa1807d0cec8d..950746fdaef103 100644 --- a/third_party/move/move-model/bytecode/ast-generator-tests/tests/conditionals.exp +++ b/third_party/move/move-model/bytecode/ast-generator-tests/tests/conditionals.exp @@ -446,24 +446,29 @@ module 0x815::m { } fun if_else_1(c: bool): u8 { let _t1; - if (c) _t1 = 1u8 else _t1 = 2u8; + if (c) _t1 = 1u8 + else _t1 = 2u8; _t1 } fun if_else_2(c: bool, d: bool): u8 { let _t2; - if (c) if (d) _t2 = 1u8 else _t2 = 2u8 else _t2 = 3u8; + if (c) if (d) _t2 = 1u8 + else _t2 = 2u8 + else _t2 = 3u8; _t2 } fun if_else_3(c: bool): u64 { let _t1; - if (c) _t1 = 1 else _t1 = 2; + if (c) _t1 = 1 + else _t1 = 2; _t1 } fun if_else_with_shard_exp(x: u64): u64 { let _t1; let x = x + x; let x = x * x; - if (x > 0) _t1 = x + 1 else _t1 = x - 1; + if (x > 0) _t1 = x + 1 + else _t1 = x - 1; _t1 } } diff --git a/third_party/move/move-model/bytecode/ast-generator-tests/tests/match.exp b/third_party/move/move-model/bytecode/ast-generator-tests/tests/match.exp index 35705886bb3e57..c09bc1dbb42f44 100644 --- a/third_party/move/move-model/bytecode/ast-generator-tests/tests/match.exp +++ b/third_party/move/move-model/bytecode/ast-generator-tests/tests/match.exp @@ -60,7 +60,7 @@ loop { loop { if (Not(_t3)) break; _t4: &Entity = self; - _t5: &u64 = select_variants m::Entity.id(_t4); + _t5: &u64 = select_variants m::Entity.Person.id(_t4); _t6: u64 = Deref(_t5); _t1: u64 = _t6; break[1] @@ -71,7 +71,7 @@ loop { loop { if (Not(_t8)) break; _t9: &Entity = self; - _t10: &u64 = select_variants m::Entity.id(_t9); + _t10: &u64 = select_variants m::Entity.Institution.id(_t9); _t11: u64 = Deref(_t10); _t1: u64 = _t11; break[1] @@ -90,7 +90,7 @@ _t2: &Entity = self; _t3: bool = test_variants m::Entity::Person(_t2); if _t3 { _t4: &Entity = self; - _t5: &u64 = select_variants m::Entity.id(_t4); + _t5: &u64 = select_variants m::Entity.Person.id(_t4); _t6: u64 = Deref(_t5); _t1: u64 = _t6 } else { @@ -98,7 +98,7 @@ if _t3 { _t8: bool = test_variants m::Entity::Institution(_t7); if _t8 { _t9: &Entity = self; - _t10: &u64 = select_variants m::Entity.id(_t9); + _t10: &u64 = select_variants m::Entity.Institution.id(_t9); _t11: u64 = Deref(_t10); _t1: u64 = _t11 } else { @@ -114,10 +114,10 @@ return _t14 { let _t1: u64; if test_variants m::Entity::Person(self) { - _t1: u64 = Deref(select_variants m::Entity.id(self)) + _t1: u64 = Deref(select_variants m::Entity.Person.id(self)) } else { if test_variants m::Entity::Institution(self) { - _t1: u64 = Deref(select_variants m::Entity.id(self)) + _t1: u64 = Deref(select_variants m::Entity.Institution.id(self)) } else { Abort(14566554180833181697) } @@ -205,7 +205,7 @@ loop { loop { if (Not(_t5)) break; _t6: &Entity = _t1; - _t7: &u64 = select_variants m::Entity.id(_t6); + _t7: &u64 = select_variants m::Entity.Person.id(_t6); _t8: u64 = Deref(_t7); _t9: u64 = 0; _t10: bool = Gt(_t8, _t9); @@ -244,7 +244,7 @@ loop { loop { if (Not(_t5)) break; _t6: &Entity = _t1; - _t7: &u64 = select_variants m::Entity.id(_t6); + _t7: &u64 = select_variants m::Entity.Person.id(_t6); _t8: u64 = Deref(_t7); _t9: u64 = 0; _t10: bool = Gt(_t8, _t9); @@ -278,7 +278,7 @@ return _t20 loop { loop { if (Not(test_variants m::Entity::Person(_t1))) break; - if (Not(Gt(Deref(select_variants m::Entity.id(_t1)), 0))) break; + if (Not(Gt(Deref(select_variants m::Entity.Person.id(_t1)), 0))) break; { let m::Entity::Person{ id: _t13 } = self; break[1] @@ -311,7 +311,9 @@ module 0x815::m { } fun id(self: &Entity): u64 { let _t1; - if (self is Person) _t1 = *self.id else if (self is Institution) _t1 = *self.id else abort 14566554180833181697; + if (self is Person) _t1 = *&self.id + else if (self is Institution) _t1 = *&self.id + else abort 14566554180833181697; _t1 } fun id2(self: Entity): u64 { @@ -320,14 +322,15 @@ module 0x815::m { 'l0: loop { loop { if (!(_t1 is Person)) break; - if (!(*_t1.id > 0)) break; + if (!(*&_t1.id > 0)) break; let Entity::Person{id: _t13} = self; break 'l0 }; if (_t1 is Institution) { let Entity::Institution{id: _t17,admin: _t18} = self; _t2 = _t17 - } else _t2 = 0; + } + else _t2 = 0; break }; _t2 diff --git a/third_party/move/move-model/bytecode/ast-generator-tests/tests/testsuite.rs b/third_party/move/move-model/bytecode/ast-generator-tests/tests/testsuite.rs index 067f8f3a8b0b72..d0428f5270d127 100644 --- a/third_party/move/move-model/bytecode/ast-generator-tests/tests/testsuite.rs +++ b/third_party/move/move-model/bytecode/ast-generator-tests/tests/testsuite.rs @@ -104,6 +104,7 @@ fn generate_output(target: &FunctionTarget, test_output: &mut String) -> Option< exp.display_for_fun(target.func_env.clone()) ); let exp = astifier::transform_assigns(target, exp); + let exp = astifier::bind_free_vars(target, exp); *test_output += &format!( "--- Assign-Transformed Generated AST\n{}\n\n", exp.display_for_fun(target.func_env.clone()) diff --git a/third_party/move/move-model/bytecode/src/astifier.rs b/third_party/move/move-model/bytecode/src/astifier.rs index 1f76329dea2e65..2f26c6b668a66c 100644 --- a/third_party/move/move-model/bytecode/src/astifier.rs +++ b/third_party/move/move-model/bytecode/src/astifier.rs @@ -1001,7 +1001,7 @@ impl Generator { None, dests, Operation::Select(*mid, *sid, field_id), - dests, + srcs, ) }, BorrowVariantField(mid, sid, variants, _inst, field_offset) => { @@ -1029,8 +1029,8 @@ impl Generator { WriteRef => { let stm = ExpData::Mutate( self.new_stm_node_id(ctx), - self.make_temp(ctx, dests[0]), self.make_temp(ctx, srcs[0]), + self.make_temp(ctx, srcs[1]), ); self.add_stm(stm) }, @@ -1106,7 +1106,11 @@ impl Generator { ctx.env().set_node_instantiation(call_id, inst.to_vec()) } let call = ExpData::Call(call_id, oper, self.make_temps(ctx, srcs.iter().copied())); - self.gen_assign(ctx, dests, call) + if !dests.is_empty() { + self.gen_assign(ctx, dests, call) + } else { + self.add_stm(call) + } } fn gen_match( @@ -1498,12 +1502,17 @@ impl<'a> IfElseTransformer<'a> { // =================================================================================== -/// A rewriter which eliminates useless assignments and introduces lets +/// A rewriter which eliminate single and uunused assignments. +/// and introduces lets /// for all free variables. pub fn transform_assigns(target: &FunctionTarget, exp: Exp) -> Exp { let usage = analyze_usage(target, exp.as_ref()); let builder = ExpBuilder::new(target.global_env()); - let exp = AssignTransformer { usage, builder }.rewrite_exp(exp); + AssignTransformer { usage, builder }.rewrite_exp(exp) +} + +/// A rewriter which binds free variables. +pub fn bind_free_vars(target: &FunctionTarget, exp: Exp) -> Exp { let usage = analyze_usage(target, exp.as_ref()); let builder = ExpBuilder::new(target.global_env()); FreeVariableBinder { diff --git a/third_party/move/move-model/src/builder/binary_module_loader.rs b/third_party/move/move-model/src/builder/binary_module_loader.rs new file mode 100644 index 00000000000000..b1ac675a945a43 --- /dev/null +++ b/third_party/move/move-model/src/builder/binary_module_loader.rs @@ -0,0 +1,629 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module implements functions which allow to add binary modules (`CompiledModule`) and +//! scripts (`CompiledScript`) to the global env. + +use crate::{ + ast::{Address, ModuleName}, + model::{ + FieldData, FieldId, FunId, FunctionData, FunctionKind, GlobalEnv, Loc, ModuleData, + ModuleId, MoveIrLoc, Parameter, StructData, StructId, StructVariant, TypeParameter, + TypeParameterKind, + }, + symbol::{Symbol, SymbolPool}, + ty::{PrimitiveType, ReferenceKind, Type}, +}; +use itertools::Itertools; +use move_binary_format::{ + file_format::{ + AbilitySet, FunctionDefinitionIndex, FunctionHandleIndex, MemberCount, SignatureToken, + StructDefinitionIndex, StructHandleIndex, TableIndex, VariantIndex, Visibility, + }, + internals::ModuleIndex, + views::{ + FieldDefinitionView, FunctionDefinitionView, FunctionHandleView, ModuleView, + StructDefinitionView, StructHandleView, ViewInternals, + }, + CompiledModule, +}; +use move_bytecode_source_map::source_map::{SourceMap, SourceName}; +use move_core_types::{account_address::AccountAddress, language_storage}; +use std::collections::BTreeMap; + +impl GlobalEnv { + /// Loads the compiled module into the environment. If the module already exists, + /// information about it will be refined according to the provided `module` + /// and `source_map` parameters. (See below about refinement.) + /// + /// If `with_dep_closure` is false, all dependencies of the module must be already present + /// in the environment. If it is true, 'stub' definitions will be added to the environment + /// for the needed declarations as they are found in the usage tables of `module`. Adding + /// stubs leads to 'partial' modules in the environment. Those can be refined in subsequent + /// `load_compiled_module` calls. + /// + /// During loading, any inconsistencies will be reported as errors to the environment. + /// This can be missing dependencies, but also mismatches between declarations found + /// in the usage tables of the loaded module and what is already present in the environment. + /// The post state of the env is guaranteed to be consistent if no errors are produced. + /// + /// The compiled module is expected to be verified. + pub fn load_compiled_module( + &mut self, + with_dep_closure: bool, + module: CompiledModule, + source_map: SourceMap, + ) -> ModuleId { + // NOTE: for the implementation, we leverage the already existing and battle-proof + // `env.attach_compiled_module` function. This one expects all the module members + // already in the environment, populated from the AST, and then attaches the bytecode + // and initializes derived data. This function here basically simulates populating + // the initial env from bytecode instead of AST, and then calls the bytecode + // attach. + let mut loader = BinaryModuleLoader::new(self, with_dep_closure, &module, &source_map); + loader.load(); + let BinaryModuleLoader { module_id, .. } = loader; + self.attach_compiled_module(module_id, module, source_map); + module_id + } +} + +struct BinaryModuleLoader<'a> { + env: &'a mut GlobalEnv, + with_dep_closure: bool, + module: ModuleView<'a, CompiledModule>, + module_name: ModuleName, + module_id: ModuleId, + module_loc: Loc, +} + +impl<'a> BinaryModuleLoader<'a> { + pub(crate) fn new( + env: &'a mut GlobalEnv, + with_dep_closure: bool, + module: &'a CompiledModule, + source_map: &'a SourceMap, + ) -> Self { + // Create the new modules needed in the environment. If `with_dep_closure` is + // not enabled, this will error if dependencies are not found. + let module = ModuleView::new(module); + let module_loc = env.to_loc(&source_map.definition_location); + let module_name = make_module_name(module.id(), env.symbol_pool()); + let vector_module_name = ModuleName::new( + Address::Numerical(AccountAddress::ONE), + env.symbol_pool().make("vector"), + ); + let mut module_id = None; + for handle in module.module_handles() { + let name = make_module_name(handle.module_id(), env.symbol_pool()); + if env.find_module(&name).is_none() { + if handle.module_id() != module.id() && !with_dep_closure { + env.error( + &module_loc, + &format!( + "while loading binary module `{}`: unresolved dependency to module `{}`", + module_name.display(env), + handle.module_id() + ), + ) + } else { + let id = ModuleId::new(env.module_data.len()); + let mut data = ModuleData::new(name.clone(), id, module_loc.clone()); + if name == vector_module_name { + // If this is `0x1::vector`, add well known vector functions. + // Those functions do not appear in the bytecode because they are + // implemented by specific instructions, but they are in the stackless + // bytecode. + add_well_known_vector_funs(env, &mut data); + } + // attach source map if this is the loaded module + if handle.module_id() == module.id() { + data.source_map = Some(source_map.clone()); + // Remember id of the loaded module + module_id = Some(ModuleId::new(env.module_data.len())) + } + env.module_data.push(data); + } + } + } + // If the vector module has not been loaded so far, do now, as it can still be + // indirectly used via instructions in the code. We could also scan the code + // if there is any usage, but it's fine to have it in the env even if not used. + if env.find_module(&vector_module_name).is_none() { + let id = ModuleId::new(env.module_data.len()); + let mut data = ModuleData::new(vector_module_name, id, module_loc.clone()); + add_well_known_vector_funs(env, &mut data); + env.module_data.push(data) + } + Self { + env, + with_dep_closure, + module, + module_name, + module_id: module_id.expect("expected Self module handle"), + module_loc, + } + } + + /// Runs the module builder, adding the resulting module to the environment. + pub(crate) fn load(&mut self) { + let struct_handle_to_def: BTreeMap = self + .module + .structs() + .enumerate() + .map(|(idx, s)| { + ( + s.handle_idx(), + StructDefinitionIndex::new(idx as TableIndex), + ) + }) + .collect(); + let fun_handle_to_def: BTreeMap = self + .module + .functions() + .enumerate() + .map(|(idx, f)| { + ( + f.handle_idx(), + FunctionDefinitionIndex::new(idx as TableIndex), + ) + }) + .collect(); + for (handle_idx, handle_view) in self.module.struct_handles().enumerate() { + let handle_idx = StructHandleIndex::new(handle_idx as TableIndex); + let def_view = struct_handle_to_def.get(&handle_idx).map(|def_idx| { + ( + *def_idx, + self.module.structs().nth(def_idx.into_index()).unwrap(), + ) + }); + if def_view.is_some() || self.with_dep_closure { + self.add_or_update_struct(handle_view, def_view) + } + } + + for (handle_idx, handle_view) in self.module.function_handles().enumerate() { + let handle_idx = FunctionHandleIndex::new(handle_idx as TableIndex); + let def_view = fun_handle_to_def.get(&handle_idx).map(|def_idx| { + ( + *def_idx, + self.module.functions().nth(def_idx.into_index()).unwrap(), + ) + }); + + if def_view.is_some() || self.with_dep_closure { + self.add_or_update_fun(handle_view, def_view) + } + } + } + + /// Construct StructData from a handle view and an optional definition view. We always + /// have a handle view, but only for structs defined in the current module also + /// a definition view. + fn add_or_update_struct( + &mut self, + handle_view: StructHandleView, + def_view: Option<(StructDefinitionIndex, StructDefinitionView)>, + ) { + let has_def = def_view.is_some(); + let module_name = make_module_name(handle_view.module_id(), self.env.symbol_pool()); + let module_id = self + .env + .find_module(&module_name) + .expect("undefined module") + .get_id(); + let struct_id = StructId::new(self.sym(handle_view.name().as_str())); + let struct_source_map = self.env.module_data[module_id.to_usize()] + .source_map + .as_ref() + .and_then(|sm| { + def_view + .as_ref() + .and_then(|(def_idx, _)| sm.get_struct_source_map(*def_idx).ok()) + }); + let loc = self.loc(struct_source_map.map(|s| s.definition_location)); + let type_params = handle_view + .type_parameters() + .iter() + .enumerate() + .map(|(i, tp)| { + let (name, loc) = self.type_param(struct_source_map.map(|s| &s.type_parameters), i); + let kind = TypeParameterKind { + abilities: tp.constraints, + is_phantom: tp.is_phantom, + }; + TypeParameter(name, kind, loc) + }) + .collect_vec(); + let mut field_data: BTreeMap = BTreeMap::new(); + let variants = if let Some((_, def_view)) = def_view { + if def_view.variant_count() > 0 { + // enum type + let mut variant_map: BTreeMap = BTreeMap::new(); + for variant in 0..def_view.variant_count() { + let variant = variant as VariantIndex; + let variant_str = def_view.variant_name(variant).as_str(); + let variant_sym = self.sym(variant_str); + variant_map.insert(variant_sym, StructVariant { + loc: loc.clone(), // source map has no info, so default to struct + attributes: vec![], + order: variant as usize, + }); + for (offset, fld) in def_view.fields_optional_variant(Some(variant)).enumerate() + { + let loc = self.loc(struct_source_map.and_then(|s| { + s.get_field_location(Some(variant), offset as MemberCount) + })); + let field_id_str = + FieldId::make_variant_field_id_str(variant_str, fld.name().as_str()); + let field_id = FieldId::new(self.sym(&field_id_str)); + field_data.insert( + field_id, + self.field_data(loc, offset, Some(variant_sym), fld), + ); + } + } + Some(variant_map) + } else { + // struct type + for (offset, fld) in def_view.fields_optional_variant(None).enumerate() { + let loc = self.loc( + struct_source_map + .and_then(|s| s.get_field_location(None, offset as MemberCount)), + ); + let field_id = FieldId::new(self.sym(fld.name().as_str())); + field_data.insert(field_id, self.field_data(loc, offset, None, fld)); + } + None + } + } else { + None + }; + + let mut new = false; + let struct_data = self.env.module_data[module_id.to_usize()] + .struct_data + .entry(struct_id) + .or_insert_with(|| { + new = true; + StructData { + abilities: handle_view.abilities(), + ..StructData::new(struct_id.symbol(), loc.clone()) + } + }); + + // Verify consistency if the type is already loaded. Can't report it now because + // the env is mut borrowed. + let has_error = !new + && (handle_view.abilities() != struct_data.abilities + || !type_params_logical_equal(&type_params, &struct_data.type_params)); + + if new || has_def { + // Update if the entry is new, or there is information which is exclusive + // to definition, like locations. + struct_data.loc = loc; + struct_data.type_params = type_params; + struct_data.field_data = field_data; + struct_data.variants = variants; + } + + if has_error { + self.error(format!( + "type `{}` has incompatible signature with already loaded type", + struct_id.symbol().display(self.env.symbol_pool()) + )) + } + } + + /// Construct field data. + fn field_data( + &self, + loc: Loc, + offset: usize, + variant: Option, + view: FieldDefinitionView, + ) -> FieldData { + FieldData { + name: self.sym(view.name().as_str()), + loc, + offset, + variant, + ty: self.ty(view.signature_token()), + } + } + + /// Construct FunctionData from a handle view and an optional definition view. We always + /// have a handle view, but only for functions defined in the current module also + /// a definition view. + fn add_or_update_fun( + &mut self, + handle_view: FunctionHandleView, + def_view: Option<( + FunctionDefinitionIndex, + FunctionDefinitionView, + )>, + ) { + let has_def = def_view.is_some(); + let module_name = make_module_name(handle_view.module_id(), self.env.symbol_pool()); + let module_id = self + .env + .find_module(&module_name) + .expect("undefined module") + .get_id(); + let fun_id = FunId::new(self.sym(handle_view.name().as_str())); + let fun_source_map = self.env.module_data[module_id.to_usize()] + .source_map + .as_ref() + .and_then(|sm| { + def_view + .as_ref() + .and_then(|(def_idx, _)| sm.get_function_source_map(*def_idx).ok()) + }); + + let loc = self.loc(fun_source_map.map(|s| s.definition_location)); + let type_params = handle_view + .type_parameters() + .iter() + .enumerate() + .map(|(i, tp)| { + let (name, loc) = self.type_param(fun_source_map.map(|s| &s.type_parameters), i); + let kind = TypeParameterKind { + abilities: *tp, + is_phantom: false, + }; + TypeParameter(name, kind, loc) + }) + .collect_vec(); + + let params = handle_view + .parameters() + .0 + .iter() + .enumerate() + .map(|(i, sign)| { + let (name, loc) = fun_source_map + .and_then(|s| { + s.get_parameter_or_local_name(i as u64) + .map(|(n, l)| (self.sym(&n), self.loc(Some(l)))) + }) + .unwrap_or_else(|| (self.sym(&format!("p{}", i)), loc.clone())); + Parameter(name, self.ty(sign), loc) + }) + .collect_vec(); + + let result_type = Type::tuple(handle_view.return_().0.iter().map(|s| self.ty(s)).collect()); + + // TODO: access specifiers + let access_specifiers = None; + let (visibility, is_native, kind) = if let Some((_, def_view)) = def_view { + ( + def_view.visibility(), + def_view.is_native(), + if def_view.is_entry() { + FunctionKind::Entry + } else { + FunctionKind::Regular + }, + ) + } else { + // We do not know this info from the handle of an imported function, so choose + // defaults + (Visibility::Public, false, FunctionKind::Regular) + }; + let mut new = false; + let fun_data = self.env.module_data[module_id.to_usize()] + .function_data + .entry(fun_id) + .or_insert_with(|| { + new = true; + FunctionData { + visibility, + is_native, + kind, + ..FunctionData::new(fun_id.symbol(), loc) + } + }); + + // Verify consistency if the type is already loaded. Can't report it now because + // the env is mut borrowed. + let has_error = !new + && (!type_params_logical_equal(&type_params, &fun_data.type_params) + || !params_logical_equal(¶ms, &fun_data.params) + || result_type != fun_data.result_type); + + if new || has_def { + // Update if the entry is new, or there is information which is exclusive + // to definition, like locations. + fun_data.type_params = type_params; + fun_data.params = params; + fun_data.access_specifiers = access_specifiers; + fun_data.result_type = result_type; + } + + if has_error { + // verify consistency if the function is already loaded + self.error(format!( + "function `{}` has incompatible signature with already loaded function", + fun_id.symbol().display(self.env.symbol_pool()) + )) + } + } + + fn ty(&self, sign: &SignatureToken) -> Type { + let resolver = |module_name, struct_sym| { + let struct_id = StructId::new(struct_sym); + if module_name == self.module_name { + self.module_id.qualified(struct_id) + } else if let Some(m) = self.env.find_module(&module_name) { + m.get_id().qualified(struct_id) + } else { + self.error(format!( + "unresolved module reference `{}`", + module_name.display_full(self.env) + )); + self.module_id.qualified(struct_id) + } + }; + Type::from_signature_token(self.env, self.module.module(), &resolver, sign) + } + + fn type_param(&self, params: Option<&Vec>, i: usize) -> (Symbol, Loc) { + let (name, loc) = params + .and_then(|p| p.get(i)) + .map(|(name, loc)| (name.to_owned(), Some(*loc))) + .unwrap_or_else(|| (format!("T{}", i), None)); + (self.sym(&name), self.loc(loc)) + } + + fn loc(&self, loc: Option) -> Loc { + loc.map(|l| self.env.to_loc(&l)) + .unwrap_or_else(|| self.module_loc.clone()) + } + + fn sym(&self, id: &str) -> Symbol { + self.env.symbol_pool().make(id) + } + + fn error(&self, msg: impl AsRef) { + self.env.error( + &self.module_loc, + &format!( + "while loading binary module `{}`: {}", + self.module_name.display(self.env), + msg.as_ref() + ), + ) + } +} + +fn make_module_name(module: language_storage::ModuleId, spool: &SymbolPool) -> ModuleName { + ModuleName::new( + Address::Numerical(module.address), + spool.make(module.name.as_str()), + ) +} + +fn type_params_logical_equal(params1: &[TypeParameter], params2: &[TypeParameter]) -> bool { + params1.len() == params2.len() && params1.iter().zip(params2).all(|(p1, p2)| p1.1 == p2.1) +} + +fn params_logical_equal(params1: &[Parameter], params2: &[Parameter]) -> bool { + params1.len() == params2.len() && params1.iter().zip(params2).all(|(p1, p2)| p1.1 == p2.1) +} + +/// Add well-known functions for vectors to module data. Those functions are instructions in +/// the bytecode and do not have associated handles, but appear as regular functions in +/// stackless bytecode and source. +#[allow(non_snake_case)] +fn add_well_known_vector_funs(env: &GlobalEnv, module_data: &mut ModuleData) { + // Vector module, add functions which are represented as bytecode + let t_params = vec![TypeParameter( + env.symbol_pool().make("T"), + TypeParameterKind::new(AbilitySet::EMPTY), + env.unknown_loc(), + )]; + let t_T = Type::TypeParameter(0); + let t_ref_T = Type::Reference(ReferenceKind::Immutable, Box::new(t_T.clone())); + let t_mut_ref_T = Type::Reference(ReferenceKind::Mutable, Box::new(t_T.clone())); + let t_vec = Type::Vector(Box::new(Type::TypeParameter(0))); + let t_ref_vec = Type::Reference(ReferenceKind::Immutable, Box::new(t_vec.clone())); + let t_mut_ref_vec = Type::Reference(ReferenceKind::Mutable, Box::new(t_vec.clone())); + let t_u64 = Type::new_prim(PrimitiveType::U64); + let mk_param = |n, t| Parameter(env.symbol_pool().make(n), t, env.unknown_loc()); + add_fun( + env, + module_data, + "empty", + t_params.clone(), + vec![], + t_vec.clone(), + ); + add_fun( + env, + module_data, + "length", + t_params.clone(), + vec![mk_param("v", t_ref_vec.clone())], + t_u64.clone(), + ); + add_fun( + env, + module_data, + "borrow", + t_params.clone(), + vec![mk_param("v", t_ref_vec), mk_param("i", t_u64.clone())], + t_ref_T.clone(), + ); + add_fun( + env, + module_data, + "borrow_mut", + t_params.clone(), + vec![ + mk_param("v", t_mut_ref_vec.clone()), + mk_param("i", t_u64.clone()), + ], + t_mut_ref_T.clone(), + ); + add_fun( + env, + module_data, + "push_back", + t_params.clone(), + vec![ + mk_param("v", t_mut_ref_vec.clone()), + mk_param("e", t_T.clone()), + ], + Type::unit(), + ); + add_fun( + env, + module_data, + "pop_back", + t_params.clone(), + vec![mk_param("v", t_mut_ref_vec.clone())], + t_T.clone(), + ); + add_fun( + env, + module_data, + "destroy_empty", + t_params.clone(), + vec![mk_param("v", t_vec)], + Type::unit(), + ); + add_fun( + env, + module_data, + "swap", + t_params.clone(), + vec![ + mk_param("v", t_mut_ref_vec), + mk_param("i", t_u64.clone()), + mk_param("j", t_u64), + ], + Type::unit(), + ); +} + +fn add_fun( + env: &GlobalEnv, + module_data: &mut ModuleData, + name: &str, + type_params: Vec, + params: Vec, + result_type: Type, +) { + let sym = env.symbol_pool().make(name); + module_data + .function_data + .insert(FunId::new(sym), FunctionData { + visibility: Visibility::Public, + is_native: true, + type_params, + params, + result_type, + ..FunctionData::new(env.symbol_pool().make(name), env.unknown_loc()) + }); +} diff --git a/third_party/move/move-model/src/builder/mod.rs b/third_party/move/move-model/src/builder/mod.rs index f18ec789c3e14f..4d963aea78cb46 100644 --- a/third_party/move/move-model/src/builder/mod.rs +++ b/third_party/move/move-model/src/builder/mod.rs @@ -2,6 +2,7 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 +pub mod binary_module_loader; mod builtins; mod exp_builder; mod macros; diff --git a/third_party/move/move-model/src/lib.rs b/third_party/move/move-model/src/lib.rs index 0a1cec2780783a..7242281bb1748a 100644 --- a/third_party/move/move-model/src/lib.rs +++ b/third_party/move/move-model/src/lib.rs @@ -67,6 +67,10 @@ pub mod symbol; pub mod ty; pub mod well_known; +pub use builder::binary_module_loader; +use move_binary_format::access::ScriptAccess; + +// // ================================================================================================= // Entry Point V2 @@ -575,6 +579,20 @@ pub fn add_move_lang_diagnostics(env: &mut GlobalEnv, diags: Diagnostics) { } } +/// Converts the given compiled script into an equivalent compiled module. This assigns +/// a unique name to the module based on the script function's name and the passed index. +/// The index must be unique w.r.t. the context of where the result shall be used +/// since the function name alone can be used by multiple scripts in the context. +pub fn convert_script_to_module(script: CompiledScript, index: usize) -> CompiledModule { + let fhd = script + .function_handles + .first() + .expect("malformed script without function"); + let name = script.identifier_at(fhd.name); + let unique_name = format!("{}_{}", name, index); + script_into_module(script, &unique_name) +} + #[allow(deprecated)] pub fn script_into_module(compiled_script: CompiledScript, name: &str) -> CompiledModule { let mut script = compiled_script; @@ -922,7 +940,7 @@ fn expansion_script_to_module(script: E::Script) -> E::ModuleDefinition { } // ================================================================================================= -// AST visitors +// AST visitors (v1 compiler infra) fn collect_lambda_lifted_functions_in_sequence( collection: &mut Vec, diff --git a/third_party/move/move-model/src/metadata.rs b/third_party/move/move-model/src/metadata.rs index c1a26833bffc80..39368cce8eba56 100644 --- a/third_party/move/move-model/src/metadata.rs +++ b/third_party/move/move-model/src/metadata.rs @@ -236,6 +236,16 @@ impl LanguageVersion { } } + /// The latest language version. + pub fn latest() -> Self { + LanguageVersion::V2_1 + } + + /// The latest stable language version. + pub fn latest_stable() -> Self { + LanguageVersion::V2_0 + } + /// Whether the language version is equal to greater than `ver` pub fn is_at_least(&self, ver: LanguageVersion) -> bool { *self >= ver diff --git a/third_party/move/move-model/src/model.rs b/third_party/move/move-model/src/model.rs index 752f6da29f2ba6..f4d9d6a1c9de2d 100644 --- a/third_party/move/move-model/src/model.rs +++ b/third_party/move/move-model/src/model.rs @@ -31,11 +31,12 @@ use crate::{ }, symbol::{Symbol, SymbolPool}, ty::{ - AbilityInference, AbilityInferer, NoUnificationContext, PrimitiveType, ReferenceKind, Type, - TypeDisplayContext, TypeUnificationAdapter, Variance, + AbilityInference, AbilityInferer, NoUnificationContext, Type, TypeDisplayContext, + TypeUnificationAdapter, Variance, }, well_known, }; +use anyhow::bail; use codespan::{ByteIndex, ByteOffset, ColumnOffset, FileId, Files, LineOffset, Location, Span}; use codespan_reporting::{ diagnostic::{Diagnostic, Label, LabelStyle, Severity}, @@ -1043,16 +1044,15 @@ impl GlobalEnv { self.internal_loc.clone() } - /// Converts a Loc as used by the move-compiler compiler to the one we are using here. + /// Converts a Loc as used by the move compiler to the one we are using here. pub fn to_loc(&self, loc: &MoveIrLoc) -> Loc { - let file_id = self.get_file_id(loc.file_hash()).unwrap_or_else(|| { - panic!( - "Unable to find source file '{}' in the environment", - loc.file_hash() - ) - }); - // Note that move-compiler doesn't use "inlined from" - Loc::new(file_id, Span::new(loc.start(), loc.end())) + if let Some(file_id) = self.get_file_id(loc.file_hash()) { + // Note that move-compiler doesn't use "inlined from" + Loc::new(file_id, Span::new(loc.start(), loc.end())) + } else { + // Cannot map this location, return unknown loc + self.unknown_loc.clone() + } } /// Converts a location back into a MoveIrLoc. If the location is not convertible, unknown @@ -1191,6 +1191,26 @@ impl GlobalEnv { self.report_diag_with_filter(writer, |d| d.severity >= severity) } + /// Helper function to report diagnostics, check for errors, and fail with a message on + /// errors. This function is idempotent and will not report the same diagnostics again. + pub fn check_diag( + &self, + error_writer: &mut W, + report_severity: Severity, + msg: &str, + ) -> anyhow::Result<()> + where + W: WriteColor + std::io::Write, + { + self.report_diag(error_writer, report_severity); + if self.has_errors() { + bail!("exiting with {}", msg); + } else { + self.clear_diag(); + Ok(()) + } + } + // Comparison of Diagnostic values that tries to match program ordering so we // can display them to the user in a more natural order. fn cmp_diagnostic(diag1: &Diagnostic, diag2: &Diagnostic) -> Ordering { @@ -2647,10 +2667,10 @@ pub struct ModuleData { pub(crate) id: ModuleId, /// Attributes attached to this module. - attributes: Vec, + pub(crate) attributes: Vec, /// Use declarations - use_decls: Vec, + pub(crate) use_decls: Vec, /// Friend declarations pub(crate) friend_decls: Vec, @@ -2703,6 +2723,33 @@ pub struct ModuleData { pub(crate) friend_modules: BTreeSet, } +impl ModuleData { + pub fn new(name: ModuleName, id: ModuleId, loc: Loc) -> Self { + Self { + name, + id, + loc, + attributes: vec![], + use_decls: vec![], + friend_decls: vec![], + compiled_module: None, + source_map: None, + named_constants: Default::default(), + struct_data: Default::default(), + struct_idx_to_id: Default::default(), + function_data: Default::default(), + function_idx_to_id: Default::default(), + spec_vars: Default::default(), + spec_funs: Default::default(), + module_spec: RefCell::new(Default::default()), + spec_block_infos: vec![], + used_modules: Default::default(), + used_modules_including_specs: RefCell::new(None), + friend_modules: Default::default(), + } + } +} + /// Represents a module environment. #[derive(Debug, Clone)] pub struct ModuleEnv<'env> { @@ -2956,6 +3003,11 @@ impl<'env> ModuleEnv<'env> { self.data.compiled_module.as_ref() } + /// Gets the underlying source map, if one is attached. + pub fn get_source_map(&'env self) -> Option<&'env SourceMap> { + self.data.source_map.as_ref() + } + /// Gets a `NamedConstantEnv` in this module by name pub fn find_named_constant(&'env self, name: Symbol) -> Option> { let id = NamedConstantId(name); @@ -3023,11 +3075,13 @@ impl<'env> ModuleEnv<'env> { /// Gets a FunctionEnv by id. pub fn into_function(self, id: FunId) -> FunctionEnv<'env> { - let data = self - .data - .function_data - .get(&id) - .unwrap_or_else(|| panic!("FunId {} undefined", id.0.display(self.symbol_pool()))); + let data = self.data.function_data.get(&id).unwrap_or_else(|| { + panic!( + "FunId {}::{} undefined", + self.get_full_name_str(), + id.0.display(self.symbol_pool()) + ) + }); FunctionEnv { module_env: self, data, @@ -3167,80 +3221,27 @@ impl<'env> ModuleEnv<'env> { /// Globalizes a signature local to this module. This requires a compiled module to be /// attached. pub fn globalize_signature(&self, sig: &SignatureToken) -> Option { - Some(self.internal_globalize_signature(self.data.compiled_module.as_ref()?, sig)) - } - - pub(crate) fn internal_globalize_signature( - &self, - module: &CompiledModule, - sig: &SignatureToken, - ) -> Type { - match sig { - SignatureToken::Bool => Type::Primitive(PrimitiveType::Bool), - SignatureToken::U8 => Type::Primitive(PrimitiveType::U8), - SignatureToken::U16 => Type::Primitive(PrimitiveType::U16), - SignatureToken::U32 => Type::Primitive(PrimitiveType::U32), - SignatureToken::U64 => Type::Primitive(PrimitiveType::U64), - SignatureToken::U128 => Type::Primitive(PrimitiveType::U128), - SignatureToken::U256 => Type::Primitive(PrimitiveType::U256), - SignatureToken::Address => Type::Primitive(PrimitiveType::Address), - SignatureToken::Signer => Type::Primitive(PrimitiveType::Signer), - SignatureToken::Reference(t) => Type::Reference( - ReferenceKind::Immutable, - Box::new(self.internal_globalize_signature(module, t)), - ), - SignatureToken::MutableReference(t) => Type::Reference( - ReferenceKind::Mutable, - Box::new(self.internal_globalize_signature(module, t)), - ), - SignatureToken::TypeParameter(index) => Type::TypeParameter(*index), - SignatureToken::Vector(bt) => { - Type::Vector(Box::new(self.internal_globalize_signature(module, bt))) - }, - SignatureToken::Struct(handle_idx) => { - let struct_view = - StructHandleView::new(module, module.struct_handle_at(*handle_idx)); - let declaring_module_env = self - .env - .find_module(&self.env.to_module_name(&struct_view.module_id())) - .expect("undefined module"); - let struct_env = declaring_module_env - .find_struct(self.env.symbol_pool.make(struct_view.name().as_str())) - .expect("undefined struct"); - Type::Struct(declaring_module_env.data.id, struct_env.get_id(), vec![]) - }, - SignatureToken::StructInstantiation(handle_idx, args) => { - let struct_view = - StructHandleView::new(module, module.struct_handle_at(*handle_idx)); - let declaring_module_env = self - .env - .find_module(&self.env.to_module_name(&struct_view.module_id())) - .expect("undefined module"); - let struct_env = declaring_module_env - .find_struct(self.env.symbol_pool.make(struct_view.name().as_str())) - .expect("undefined struct"); - Type::Struct( - declaring_module_env.data.id, - struct_env.get_id(), - self.internal_globalize_signatures(module, args), - ) - }, - } + let struct_resolver = |module_name: ModuleName, struct_name: Symbol| { + let declaring_module_env = self + .env + .find_module(&module_name) + .expect("expected defined module name"); + let struct_env = declaring_module_env + .find_struct(struct_name) + .expect("expected defined struct name"); + declaring_module_env.get_id().qualified(struct_env.get_id()) + }; + Some(Type::from_signature_token( + self.env, + self.data.compiled_module.as_ref()?, + &struct_resolver, + sig, + )) } /// Globalizes a list of signatures. pub fn globalize_signatures(&self, sigs: &[SignatureToken]) -> Option> { - Some(self.internal_globalize_signatures(self.data.compiled_module.as_ref()?, sigs)) - } - - pub(crate) fn internal_globalize_signatures( - &self, - module: &CompiledModule, - sigs: &[SignatureToken], - ) -> Vec { - sigs.iter() - .map(|sig| self.internal_globalize_signature(module, sig)) - .collect() + sigs.iter().map(|s| self.globalize_signature(s)).collect() } /// Gets a list of type actuals associated with the index in the bytecode, if @@ -3444,6 +3445,24 @@ pub struct StructData { pub is_native: bool, } +impl StructData { + pub fn new(name: Symbol, loc: Loc) -> Self { + Self { + name, + loc, + def_idx: None, + attributes: vec![], + type_params: vec![], + abilities: AbilitySet::ALL, + spec_var_opt: None, + field_data: Default::default(), + variants: None, + spec: RefCell::new(Default::default()), + is_native: false, + } + } +} + #[derive(Debug)] pub(crate) struct StructVariant { pub(crate) loc: Loc, @@ -3457,7 +3476,7 @@ pub struct StructEnv<'env> { pub module_env: ModuleEnv<'env>, /// Reference to the struct data. - data: &'env StructData, + pub data: &'env StructData, } impl<'env> StructEnv<'env> { @@ -3814,7 +3833,17 @@ impl<'env> FieldEnv<'env> { /// Gets the id of this field. pub fn get_id(&self) -> FieldId { - FieldId(self.data.name) + if let Some(variant) = self.get_variant() { + // TODO: this is inefficient and we may want to cache it + let spool = self.struct_env.symbol_pool(); + let id_str = FieldId::make_variant_field_id_str( + spool.string(variant).as_str(), + spool.string(self.data.name).as_str(), + ); + FieldId(spool.make(&id_str)) + } else { + FieldId(self.data.name) + } } /// Returns true if this is a positional field. Identified by that the name @@ -4048,6 +4077,18 @@ pub struct FunctionLoc { pub(crate) result_type_loc: Loc, } +impl FunctionLoc { + pub fn from_single(loc: Loc) -> Self { + // TODO: consider making non-full locations optional, as we do not always have this + // information. + Self { + full: loc.clone(), + id_loc: loc.clone(), + result_type_loc: loc, + } + } +} + #[derive(Debug)] pub struct FunctionData { /// Name of this function. @@ -4109,6 +4150,31 @@ pub struct FunctionData { pub(crate) transitive_closure_of_called_funs: RefCell>>>, } +impl FunctionData { + pub fn new(name: Symbol, loc: Loc) -> Self { + Self { + name, + loc: FunctionLoc::from_single(loc), + def_idx: None, + handle_idx: None, + visibility: Default::default(), + has_package_visibility: false, + is_native: false, + kind: FunctionKind::Regular, + attributes: vec![], + type_params: vec![], + params: vec![], + result_type: Type::unit(), + access_specifiers: None, + spec: RefCell::new(Default::default()), + def: None, + called_funs: None, + calling_funs: RefCell::new(None), + transitive_closure_of_called_funs: RefCell::new(None), + } + } +} + /// Kind of a function, #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum FunctionKind { diff --git a/third_party/move/move-model/src/sourcifier.rs b/third_party/move/move-model/src/sourcifier.rs index e1e6b936cb7389..18c02071483997 100644 --- a/third_party/move/move-model/src/sourcifier.rs +++ b/third_party/move/move-model/src/sourcifier.rs @@ -131,12 +131,15 @@ impl<'a> Sourcifier<'a> { fun_env.get_result_type().display(&tctx) ) } - emit!(self.writer, " "); - self.print_access_specifiers(&tctx, &fun_env); + if fun_env.get_access_specifiers().is_none() && !fun_env.is_native() { + // Add a space so we open the code block with " {" on the same line. + emit!(self.writer, " "); + } else { + self.print_access_specifiers(&tctx, &fun_env); + } if let Some(def) = def { // A sequence or block is already automatically printed in braces with indent - let requires_braces = - !matches!(def.as_ref(), ExpData::Sequence(..) | ExpData::Block(..)); + let requires_braces = !Self::is_braced(def); let exp_sourcifier = ExpSourcifier::for_fun(self, &fun_env, def); if requires_braces { self.print_block(|| { @@ -154,11 +157,19 @@ impl<'a> Sourcifier<'a> { } } + fn is_braced(exp: &Exp) -> bool { + match exp.as_ref() { + ExpData::Sequence(_, exps) => exps.len() != 1 || Self::is_braced(&exps[0]), + ExpData::Block(..) => true, + _ => false, + } + } + fn print_access_specifiers(&self, tctx: &TypeDisplayContext, fun: &FunctionEnv) { if let Some(specs) = fun.get_access_specifiers() { - emitln!(self.writer); self.writer.indent(); for spec in specs { + emitln!(self.writer); if spec.negated { emit!(self.writer, "!") } @@ -214,11 +225,11 @@ impl<'a> Sourcifier<'a> { self.sym(*sym) ), } - emitln!(self.writer, ")") + emit!(self.writer, ")") } } self.writer.unindent(); - emitln!(self.writer); + emitln!(self.writer) } } @@ -605,7 +616,12 @@ impl<'a> ExpSourcifier<'a> { } } else { self.print_exp(Prio::General, is_result, if_exp); - emit!(self.wr(), " else "); + if !Sourcifier::is_braced(else_exp) { + emitln!(self.wr()); + emit!(self.wr(), "else ") + } else { + emit!(self.wr(), " else "); + } self.print_exp(Prio::General, is_result, else_exp); } }) @@ -770,16 +786,31 @@ impl<'a> ExpSourcifier<'a> { self.parenthesize(context_prio, Prio::Postfix, || { let struct_env = self.env().get_module(*mid).into_struct(*sid); let field_env = struct_env.get_field(*fid); + let result_ty = self.env().get_node_type(id); + if result_ty.is_immutable_reference() { + emit!(self.wr(), "&") + } else if result_ty.is_mutable_reference() { + emit!(self.wr(), "&mut ") + } self.print_exp(Prio::Postfix, false, &args[0]); emit!(self.wr(), ".{}", self.sym(field_env.get_name())) }) }, - Operation::SelectVariants(_, _, fids) => { + Operation::SelectVariants(mid, sid, fids) => { self.parenthesize(context_prio, Prio::Postfix, || { - self.print_exp(Prio::Postfix, false, &args[0]); + let struct_env = self.env().get_module(*mid).into_struct(*sid); // All field names are the same, so we can choose one representative // on source level. - emit!(self.wr(), ".{}", self.sym(fids[0].symbol())); + let fid = fids[0]; + let field_env = struct_env.get_field(fid); + let result_ty = self.env().get_node_type(id); + if result_ty.is_immutable_reference() { + emit!(self.wr(), "&") + } else if result_ty.is_mutable_reference() { + emit!(self.wr(), "&mut ") + } + self.print_exp(Prio::Postfix, false, &args[0]); + emit!(self.wr(), ".{}", self.sym(field_env.get_name())) }) }, Operation::TestVariants(_, _, variants) => { diff --git a/third_party/move/move-model/src/ty.rs b/third_party/move/move-model/src/ty.rs index 07ee9d11abba8a..4407bbba93c5b4 100644 --- a/third_party/move/move-model/src/ty.rs +++ b/third_party/move/move-model/src/ty.rs @@ -14,9 +14,14 @@ use crate::{ symbol::Symbol, }; use itertools::Itertools; -use move_binary_format::file_format::{Ability, AbilitySet, TypeParameterIndex}; #[allow(deprecated)] use move_binary_format::normalized::Type as MType; +use move_binary_format::{ + access::ModuleAccess, + file_format::{Ability, AbilitySet, SignatureToken, TypeParameterIndex}, + views::StructHandleView, + CompiledModule, +}; use move_core_types::{ language_storage::{StructTag, TypeTag}, u256::U256, @@ -1308,6 +1313,68 @@ impl Type { } } + /// Generates a type from a signature token in the context of the given binary module. + /// The `env` is only passed for general purposes, type name resolution is done + /// via a special resolver function, allowing to work with partially populated + /// environments. + pub fn from_signature_token( + env: &GlobalEnv, + module: &CompiledModule, + struct_resolver: &impl Fn(ModuleName, Symbol) -> QualifiedId, + sig: &SignatureToken, + ) -> Self { + match sig { + SignatureToken::Bool => Type::Primitive(PrimitiveType::Bool), + SignatureToken::U8 => Type::Primitive(PrimitiveType::U8), + SignatureToken::U16 => Type::Primitive(PrimitiveType::U16), + SignatureToken::U32 => Type::Primitive(PrimitiveType::U32), + SignatureToken::U64 => Type::Primitive(PrimitiveType::U64), + SignatureToken::U128 => Type::Primitive(PrimitiveType::U128), + SignatureToken::U256 => Type::Primitive(PrimitiveType::U256), + SignatureToken::Address => Type::Primitive(PrimitiveType::Address), + SignatureToken::Signer => Type::Primitive(PrimitiveType::Signer), + SignatureToken::Reference(t) => Type::Reference( + ReferenceKind::Immutable, + Box::new(Self::from_signature_token(env, module, struct_resolver, t)), + ), + SignatureToken::MutableReference(t) => Type::Reference( + ReferenceKind::Mutable, + Box::new(Self::from_signature_token(env, module, struct_resolver, t)), + ), + SignatureToken::TypeParameter(index) => Type::TypeParameter(*index), + SignatureToken::Vector(bt) => Type::Vector(Box::new(Self::from_signature_token( + env, + module, + struct_resolver, + bt, + ))), + SignatureToken::Struct(handle_idx) => { + let struct_view = + StructHandleView::new(module, module.struct_handle_at(*handle_idx)); + let struct_id = struct_resolver( + env.to_module_name(&struct_view.module_id()), + env.symbol_pool.make(struct_view.name().as_str()), + ); + Type::Struct(struct_id.module_id, struct_id.id, vec![]) + }, + SignatureToken::StructInstantiation(handle_idx, args) => { + let struct_view = + StructHandleView::new(module, module.struct_handle_at(*handle_idx)); + let struct_id = struct_resolver( + env.to_module_name(&struct_view.module_id()), + env.symbol_pool.make(struct_view.name().as_str()), + ); + Type::Struct( + struct_id.module_id, + struct_id.id, + args.iter() + .map(|t| Self::from_signature_token(env, module, struct_resolver, t)) + .collect(), + ) + }, + } + } + /// Get the unbound type variables in the type. pub fn get_vars(&self) -> BTreeSet { let mut vars = BTreeSet::new(); diff --git a/third_party/move/move-prover/tests/sources/functional/unused_schema.v2_exp b/third_party/move/move-prover/tests/sources/functional/unused_schema.v2_exp deleted file mode 100644 index ad59d3ec627f56..00000000000000 --- a/third_party/move/move-prover/tests/sources/functional/unused_schema.v2_exp +++ /dev/null @@ -1,9 +0,0 @@ -note: unused schema TestUnusedSchema::AddsThree - ┌─ tests/sources/functional/unused_schema.move:22:5 - │ -22 │ ╭ spec schema AddsThree { -23 │ │ i: num; -24 │ │ result: num; -25 │ │ ensures result == i + 3; -26 │ │ } - │ ╰─────^ diff --git a/third_party/move/tools/move-decompiler/Cargo.toml b/third_party/move/tools/move-decompiler/Cargo.toml new file mode 100644 index 00000000000000..6ebeff4ab088ba --- /dev/null +++ b/third_party/move/tools/move-decompiler/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "move-decompiler" +version = "0.1.0" +authors = ["Aptos Labs"] +description = "Move Decompiler" +repository = "https://github.com/aptos-labs/aptos-core" +homepage = "https://aptosfoundation.org/" +license = "Apache-2.0" +publish = false +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +clap = { workspace = true, features = ["derive"] } +codespan = { workspace = true } +codespan-reporting = { workspace = true, features = ["serde", "serialization"] } +colored = { workspace = true } + +bcs = { workspace = true } +move-binary-format = { workspace = true } +move-bytecode-source-map = { workspace = true } +move-command-line-common = { workspace = true } +move-core-types = { workspace = true } +move-model = { workspace = true } +move-stackless-bytecode = { workspace = true } + +[dev-dependencies] +datatest-stable = { workspace = true } +move-compiler-v2 = { workspace = true } +move-prover-test-utils = { workspace = true } + +[features] +default = [] + +[[test]] +name = "testsuite" +harness = false + +[lib] +doctest = false diff --git a/third_party/move/tools/move-decompiler/src/lib.rs b/third_party/move/tools/move-decompiler/src/lib.rs new file mode 100644 index 00000000000000..e5d2a5158c569a --- /dev/null +++ b/third_party/move/tools/move-decompiler/src/lib.rs @@ -0,0 +1,208 @@ +// Copyright (c) Aptos Foundation +// Parts of the project are originally copyright (c) Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Context; +use clap::Parser; +use codespan::Span; +use codespan_reporting::{diagnostic::Severity, term::termcolor::WriteColor}; +use move_binary_format::{file_format::CompiledScript, CompiledModule}; +use move_bytecode_source_map::source_map::SourceMap; +use move_command_line_common::files::FileHash; +use move_model::{ + metadata::LanguageVersion, + model::{GlobalEnv, Loc}, + sourcifier::Sourcifier, +}; +use move_stackless_bytecode::{ + astifier, + function_target_pipeline::{FunctionTargetsHolder, FunctionVariant}, +}; +use std::{collections::BTreeMap, fs, io::Write, mem, path::Path, rc::Rc}; + +#[derive(Parser, Clone, Debug, Default)] +#[clap(author, version, about)] +pub struct Options { + /// Language version of the produced output. The decompiler will not produce source + /// which requires a newer version. If the decompiled bytecode requires + /// language features beyond this version, decompilation will fail. + #[clap(long, value_parser = clap::value_parser!(LanguageVersion))] + pub language_version: Option, + + /// By default, the decompiler detects conditionals (`if` and `match`) in + /// the bytecode. With this flag, the behavior can be turned off. + #[clap(long, default_value = "false")] + pub no_conditionals: bool, + + /// By default, the decompiler eliminates single assignments, and reconstructs + /// expressions from the bytecode. With this flag, the behavior can be turned off. + #[clap(long, default_value = "false")] + pub no_expressions: bool, + + /// Directory where to place decompiled sources. + #[clap(short, long, default_value = "")] + pub output_dir: String, + + /// Ending to use for decompiled sources. Defaults to `mv.move` + #[clap(short, long, default_value = "mv.move")] + pub ending: String, + + /// Whether the input files should be interpreted as scripts. Default is + /// to interpret them as modules. + #[clap(long, short, default_value = "false")] + pub script: bool, + + /// Directory where to search for source maps. Source maps are identified + /// by the base name of the bytecode file with suffix `mvsm`. + #[clap(long, default_value = "")] + pub source_map_dir: String, + + /// Input files, interpreted as serialized versions of a module + /// or script, dependening on the `--script` flag. + pub inputs: Vec, +} + +/// Represents an instance of a decompiler. The decompiler can be run end-to-end operating +/// on files, or incrementally without already deserialized modules and scripts. +pub struct Decompiler { + options: Options, + env: GlobalEnv, +} + +impl Decompiler { + /// Creates a new decompiler. + pub fn new(options: Options) -> Self { + Self { + options, + env: GlobalEnv::new(), + } + } + + /// Performs a run of the decompiler as specified by the options. This reads all + /// bytecode input files and runs the stages of decompilation for each, producing + /// output source files. + pub fn run(mut self, error_writer: &mut W) -> anyhow::Result<()> + where + W: Write + WriteColor, + { + for input_path in mem::take(&mut self.options.inputs) { + let input_file = Path::new(Path::new(&input_path).file_name().unwrap_or_default()) + .to_string_lossy() + .to_string(); + let bytes = + fs::read(&input_path).with_context(|| format!("reading `{}`", input_path))?; + let output = if self.options.script { + let script = CompiledScript::deserialize(&bytes) + .with_context(|| format!("deserializing script `{}`", input_path))?; + let source_map = self.get_source_map(&input_path, &input_file, &bytes); + self.decompile_script(script, source_map) + } else { + let module = CompiledModule::deserialize(&bytes) + .with_context(|| format!("deserializing module `{}`", input_path))?; + let source_map = self.get_source_map(&input_path, &input_file, &bytes); + self.decompile_module(module, source_map) + }; + self.env.check_diag( + error_writer, + Severity::Warning, + &format!("decompiling `{}`", input_path), + )?; + let out_file = Path::new(&self.options.output_dir) + .join(&input_file) + .with_extension(&self.options.ending); + fs::write(&out_file, output) + .with_context(|| format!("writing `{}`", out_file.display()))?; + } + Ok(()) + } + + fn get_source_map(&mut self, input_path: &str, input_file: &str, bytes: &[u8]) -> SourceMap { + let path = Path::new(&self.options.source_map_dir) + .join(input_file) + .with_extension("mvsm"); + match fs::read(path) { + Ok(bytes) => bcs::from_bytes::(&bytes) + .unwrap_or_else(|_| self.empty_source_map(input_path, &bytes)), + _ => self.empty_source_map(input_path, bytes), + } + } + + /// Decompiles the given binary module. A source map must be provided for error + /// reporting; use `self.empty_source_map` to create one if none is available. + pub fn decompile_module(&mut self, module: CompiledModule, source_map: SourceMap) -> String { + // Load module into environment, creating stubs for any missing dependencies + let module_id = self + .env + .load_compiled_module(/*with_dep_closure*/ true, module, source_map); + if self.env.has_errors() { + return String::default(); + } + + // Create FunctionTargetsHolder with stackless bytecode for all functions in the module, + let module_env = self.env.get_module(module_id); + let mut targets = FunctionTargetsHolder::default(); + for func_env in module_env.get_functions() { + targets.add_target(&func_env) + } + + // Generate AST for all function targets. + for fun_id in targets.get_funs() { + let fun_env = self.env.get_function(fun_id); + let target = targets.get_target(&fun_env, &FunctionVariant::Baseline); + if target.get_bytecode().is_empty() { + continue; + } + if let Some(def) = astifier::generate_ast_raw(&target) { + let def = if self.options.no_conditionals { + def + } else { + astifier::transform_conditionals(&target, def) + }; + let def = if self.options.no_expressions { + def + } else { + astifier::transform_assigns(&target, def) + }; + // The next step must always happen to create valid Move + let def = astifier::bind_free_vars(&target, def); + self.env.set_function_def(fun_env.get_qualified_id(), def) + } + } + + // Render the module as source + let sourcifier = Sourcifier::new(&self.env); + sourcifier.print_module(module_id); + sourcifier.result() + } + + /// Decompiles the give binary script. Same as `decompile_module` but for scripts. + pub fn decompile_script(&mut self, script: CompiledScript, source_map: SourceMap) -> String { + self.decompile_module( + move_model::convert_script_to_module(script, self.env.get_module_count()), + source_map, + ) + } + + /// Return the environment the decompiler has built so far. + pub fn env(&self) -> &GlobalEnv { + &self.env + } + + /// Creates an empty source map, with a location for the specified module. This virtual + /// location is constructed from `name` (the file name of the module) and `contents` + /// (the bytes). The internal logic of the environments needs currently both pieces + /// information to create new locations (related to differences in Loc representation + /// with legacy code). + pub fn empty_source_map(&mut self, name: &str, unique_bytes: &[u8]) -> SourceMap { + let file_id = self.env.add_source( + FileHash::new_from_bytes(unique_bytes), + Rc::new(BTreeMap::new()), + name, + "", + true, + true, + ); + let loc = Loc::new(file_id, Span::new(0, 0)); + SourceMap::new(self.env.to_ir_loc(&loc), None) + } +} diff --git a/third_party/move/tools/move-decompiler/src/main.rs b/third_party/move/tools/move-decompiler/src/main.rs new file mode 100644 index 00000000000000..c6c34444de3bb2 --- /dev/null +++ b/third_party/move/tools/move-decompiler/src/main.rs @@ -0,0 +1,16 @@ +// Copyright (c) Aptos Foundation +// Parts of the project are originally copyright (c) Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; +use move_decompiler::{Decompiler, Options}; + +fn main() { + let options = Options::parse(); + let mut error_writer = StandardStream::stderr(ColorChoice::Auto); + if let Err(e) = Decompiler::new(options).run(&mut error_writer) { + eprintln!("error: {:#}", e); + std::process::exit(1) + } +} diff --git a/third_party/move/tools/move-decompiler/tests/bit_vector.exp b/third_party/move/tools/move-decompiler/tests/bit_vector.exp new file mode 100644 index 00000000000000..887431f3069af6 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/bit_vector.exp @@ -0,0 +1,65 @@ + +module 0x1::bit_vector { + struct BitVector has copy, drop, store { + length: u64, + bit_field: vector, + } + public fun length(self: &BitVector): u64 { + 0x1::vector::length(&self.bit_field) + } + public fun is_index_set(self: &BitVector, bit_index: u64): bool { + loop { + if (bit_index < 0x1::vector::length(&self.bit_field)) break; + abort 131072 + }; + *0x1::vector::borrow(&self.bit_field, bit_index) + } + public fun longest_set_sequence_starting_at(self: &BitVector, start_index: u64): u64 { + let _t3; + if (start_index < *&self.length) _t3 = start_index else { + abort 131072; + loop { + let _t2 = *&self.length; + if (_t3 < *&self.length) if (!!is_index_set(self, _t3)) { + let _t2 = 1; + _t3 = _t3 + *&self.length; + continue + }; + break + } + }; + _t3 - start_index + } + public fun new(length: u64): BitVector { + let _t2; + let _t3; + let _t1 = 0; + loop { + if (length > _t1) break; + abort 131073 + }; + if (length < 1024) _t3 = 0x1::vector::empty() else { + abort 131073; + while (_t2 < length) { + 0x1::vector::push_back(&mut _t3, false); + let _t2 = _t2 + 1024; + continue + } + }; + BitVector{length: length,bit_field: _t3} + } + public fun set(self: &mut BitVector, bit_index: u64) { + loop { + if (bit_index < 0x1::vector::length(&self.bit_field)) break; + abort 131072 + }; + *0x1::vector::borrow_mut(&mut self.bit_field, bit_index) = true; + } + public fun unset(self: &mut BitVector, bit_index: u64) { + loop { + if (bit_index < 0x1::vector::length(&self.bit_field)) break; + abort 131072 + }; + *0x1::vector::borrow_mut(&mut self.bit_field, bit_index) = false; + } +} diff --git a/third_party/move/tools/move-decompiler/tests/bit_vector.move b/third_party/move/tools/move-decompiler/tests/bit_vector.move new file mode 100644 index 00000000000000..227c052c931287 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/bit_vector.move @@ -0,0 +1,241 @@ +module std::bit_vector { + use std::vector; + + /// The provided index is out of bounds + const EINDEX: u64 = 0x20000; + /// An invalid length of bitvector was given + const ELENGTH: u64 = 0x20001; + + const WORD_SIZE: u64 = 1; + /// The maximum allowed bitvector size + const MAX_SIZE: u64 = 1024; + + spec BitVector { + invariant length == len(bit_field); + } + + struct BitVector has copy, drop, store { + length: u64, + bit_field: vector, + } + + public fun new(length: u64): BitVector { + assert!(length > 0, ELENGTH); + assert!(length < MAX_SIZE, ELENGTH); + let counter = 0; + let bit_field = vector::empty(); + while ({spec { + invariant counter <= length; + invariant len(bit_field) == counter; + }; + (counter < length)}) { + vector::push_back(&mut bit_field, false); + counter = counter + 1; + }; + spec { + assert counter == length; + assert len(bit_field) == length; + }; + + BitVector { + length, + bit_field, + } + } + spec new { + include NewAbortsIf; + ensures result.length == length; + ensures len(result.bit_field) == length; + } + spec schema NewAbortsIf { + length: u64; + aborts_if length <= 0 with ELENGTH; + aborts_if length >= MAX_SIZE with ELENGTH; + } + + /// Set the bit at `bit_index` in the `self` regardless of its previous state. + public fun set(self: &mut BitVector, bit_index: u64) { + assert!(bit_index < vector::length(&self.bit_field), EINDEX); + let x = vector::borrow_mut(&mut self.bit_field, bit_index); + *x = true; + } + spec set { + include SetAbortsIf; + ensures self.bit_field[bit_index]; + } + spec schema SetAbortsIf { + self: BitVector; + bit_index: u64; + aborts_if bit_index >= length(self) with EINDEX; + } + + /// Unset the bit at `bit_index` in the `self` regardless of its previous state. + public fun unset(self: &mut BitVector, bit_index: u64) { + assert!(bit_index < vector::length(&self.bit_field), EINDEX); + let x = vector::borrow_mut(&mut self.bit_field, bit_index); + *x = false; + } + spec unset { + include UnsetAbortsIf; + ensures !self.bit_field[bit_index]; + } + spec schema UnsetAbortsIf { + self: BitVector; + bit_index: u64; + aborts_if bit_index >= length(self) with EINDEX; + } + + /// Shift the `self` left by `amount`. If `amount` is greater than the + /// bitvector's length the bitvector will be zeroed out. + /* !!!!!! TODO: currently crashes astifier + public fun shift_left(self: &mut BitVector, amount: u64) { + if (amount >= self.length) { + vector::for_each_mut(&mut self.bit_field, |elem| { + *elem = false; + }); + } else { + let i = amount; + + while (i < self.length) { + if (is_index_set(self, i)) set(self, i - amount) + else unset(self, i - amount); + i = i + 1; + }; + + i = self.length - amount; + + while (i < self.length) { + unset(self, i); + i = i + 1; + }; + } + } + spec shift_left { + // TODO: set to false because data invariant cannot be proved with inline function. Will remove it once inline is supported + pragma verify = false; + } + */ + + /// Return the value of the bit at `bit_index` in the `self`. `true` + /// represents "1" and `false` represents a 0 + public fun is_index_set(self: &BitVector, bit_index: u64): bool { + assert!(bit_index < vector::length(&self.bit_field), EINDEX); + *vector::borrow(&self.bit_field, bit_index) + } + spec is_index_set { + include IsIndexSetAbortsIf; + ensures result == self.bit_field[bit_index]; + } + spec schema IsIndexSetAbortsIf { + self: BitVector; + bit_index: u64; + aborts_if bit_index >= length(self) with EINDEX; + } + spec fun spec_is_index_set(self: BitVector, bit_index: u64): bool { + if (bit_index >= length(self)) { + false + } else { + self.bit_field[bit_index] + } + } + + /// Return the length (number of usable bits) of this bitvector + public fun length(self: &BitVector): u64 { + vector::length(&self.bit_field) + } + + /// Returns the length of the longest sequence of set bits starting at (and + /// including) `start_index` in the `bitvector`. If there is no such + /// sequence, then `0` is returned. + public fun longest_set_sequence_starting_at(self: &BitVector, start_index: u64): u64 { + assert!(start_index < self.length, EINDEX); + let index = start_index; + + // Find the greatest index in the vector such that all indices less than it are set. + while ({ + spec { + invariant index >= start_index; + invariant index == start_index || is_index_set(self, index - 1); + invariant index == start_index || index - 1 < vector::length(self.bit_field); + invariant forall j in start_index..index: is_index_set(self, j); + invariant forall j in start_index..index: j < vector::length(self.bit_field); + }; + index < self.length + }) { + if (!is_index_set(self, index)) break; + index = index + 1; + }; + + index - start_index + } + + spec longest_set_sequence_starting_at(self: &BitVector, start_index: u64): u64 { + aborts_if start_index >= self.length; + ensures forall i in start_index..result: is_index_set(self, i); + } + + #[test_only] + public fun word_size(): u64 { + WORD_SIZE + } + + #[verify_only] + public fun shift_left_for_verification_only(self: &mut BitVector, amount: u64) { + if (amount >= self.length) { + let len = vector::length(&self.bit_field); + let i = 0; + while ({ + spec { + invariant len == self.length; + invariant forall k in 0..i: !self.bit_field[k]; + invariant forall k in i..self.length: self.bit_field[k] == old(self).bit_field[k]; + }; + i < len + }) { + let elem = vector::borrow_mut(&mut self.bit_field, i); + *elem = false; + i = i + 1; + }; + } else { + let i = amount; + + while ({ + spec { + invariant i >= amount; + invariant self.length == old(self).length; + invariant forall j in amount..i: old(self).bit_field[j] == self.bit_field[j - amount]; + invariant forall j in (i-amount)..self.length : old(self).bit_field[j] == self.bit_field[j]; + invariant forall k in 0..i-amount: self.bit_field[k] == old(self).bit_field[k + amount]; + }; + i < self.length + }) { + if (is_index_set(self, i)) set(self, i - amount) + else unset(self, i - amount); + i = i + 1; + }; + + + i = self.length - amount; + + while ({ + spec { + invariant forall j in self.length - amount..i: !self.bit_field[j]; + invariant forall k in 0..self.length - amount: self.bit_field[k] == old(self).bit_field[k + amount]; + invariant i >= self.length - amount; + }; + i < self.length + }) { + unset(self, i); + i = i + 1; + } + } + } + spec shift_left_for_verification_only { + aborts_if false; + ensures amount >= self.length ==> (forall k in 0..self.length: !self.bit_field[k]); + ensures amount < self.length ==> + (forall i in self.length - amount..self.length: !self.bit_field[i]); + ensures amount < self.length ==> + (forall i in 0..self.length - amount: self.bit_field[i] == old(self).bit_field[i + amount]); + } +} diff --git a/third_party/move/tools/move-decompiler/tests/error.exp b/third_party/move/tools/move-decompiler/tests/error.exp new file mode 100644 index 00000000000000..d9c8e15227dd23 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/error.exp @@ -0,0 +1,42 @@ + +module 0x1::error { + public fun aborted(r: u64): u64 { + canonical(7, r) + } + public fun canonical(category: u64, reason: u64): u64 { + (category << 16u8) + reason + } + public fun already_exists(r: u64): u64 { + canonical(8, r) + } + public fun internal(r: u64): u64 { + canonical(11, r) + } + public fun invalid_argument(r: u64): u64 { + canonical(1, r) + } + public fun invalid_state(r: u64): u64 { + canonical(3, r) + } + public fun not_found(r: u64): u64 { + canonical(6, r) + } + public fun not_implemented(r: u64): u64 { + canonical(12, r) + } + public fun out_of_range(r: u64): u64 { + canonical(2, r) + } + public fun permission_denied(r: u64): u64 { + canonical(5, r) + } + public fun resource_exhausted(r: u64): u64 { + canonical(9, r) + } + public fun unauthenticated(r: u64): u64 { + canonical(4, r) + } + public fun unavailable(r: u64): u64 { + canonical(13, r) + } +} diff --git a/third_party/move/tools/move-decompiler/tests/error.move b/third_party/move/tools/move-decompiler/tests/error.move new file mode 100644 index 00000000000000..1facaf01da6852 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/error.move @@ -0,0 +1,88 @@ +/// This module defines a set of canonical error codes which are optional to use by applications for the +/// `abort` and `assert!` features. +/// +/// Canonical error codes use the 3 lowest bytes of the u64 abort code range (the upper 5 bytes are free for other use). +/// Of those, the highest byte represents the *error category* and the lower two bytes the *error reason*. +/// Given an error category `0x1` and a reason `0x3`, a canonical abort code looks as `0x10003`. +/// +/// A module can use a canonical code with a constant declaration of the following form: +/// +/// ``` +/// /// An invalid ASCII character was encountered when creating a string. +/// const EINVALID_CHARACTER: u64 = 0x010003; +/// ``` +/// +/// This code is both valid in the worlds with and without canonical errors. It can be used as a plain module local +/// error reason understand by the existing error map tooling, or as a canonical code. +/// +/// The actual canonical categories have been adopted from Google's canonical error codes, which in turn are derived +/// from Unix error codes [see here](https://cloud.google.com/apis/design/errors#handling_errors). Each code has an +/// associated HTTP error code which can be used in REST apis. The mapping from error code to http code is not 1:1; +/// error codes here are a bit richer than HTTP codes. +module std::error { + + /// Caller specified an invalid argument (http: 400) + const INVALID_ARGUMENT: u64 = 0x1; + + /// An input or result of a computation is out of range (http: 400) + const OUT_OF_RANGE: u64 = 0x2; + + /// The system is not in a state where the operation can be performed (http: 400) + const INVALID_STATE: u64 = 0x3; + + /// Request not authenticated due to missing, invalid, or expired auth token (http: 401) + const UNAUTHENTICATED: u64 = 0x4; + + /// client does not have sufficient permission (http: 403) + const PERMISSION_DENIED: u64 = 0x5; + + /// A specified resource is not found (http: 404) + const NOT_FOUND: u64 = 0x6; + + /// Concurrency conflict, such as read-modify-write conflict (http: 409) + const ABORTED: u64 = 0x7; + + /// The resource that a client tried to create already exists (http: 409) + const ALREADY_EXISTS: u64 = 0x8; + + /// Out of gas or other forms of quota (http: 429) + const RESOURCE_EXHAUSTED: u64 = 0x9; + + /// Request cancelled by the client (http: 499) + const CANCELLED: u64 = 0xA; + + /// Internal error (http: 500) + const INTERNAL: u64 = 0xB; + + /// Feature not implemented (http: 501) + const NOT_IMPLEMENTED: u64 = 0xC; + + /// The service is currently unavailable. Indicates that a retry could solve the issue (http: 503) + const UNAVAILABLE: u64 = 0xD; + + /// Construct a canonical error code from a category and a reason. + public fun canonical(category: u64, reason: u64): u64 { + (category << 16) + reason + } + spec canonical { + pragma opaque = true; + let shl_res = category << 16; + ensures [concrete] result == shl_res + reason; + aborts_if [abstract] false; + ensures [abstract] result == category; + } + + /// Functions to construct a canonical error code of the given category. + public fun invalid_argument(r: u64): u64 { canonical(INVALID_ARGUMENT, r) } + public fun out_of_range(r: u64): u64 { canonical(OUT_OF_RANGE, r) } + public fun invalid_state(r: u64): u64 { canonical(INVALID_STATE, r) } + public fun unauthenticated(r: u64): u64 { canonical(UNAUTHENTICATED, r) } + public fun permission_denied(r: u64): u64 { canonical(PERMISSION_DENIED, r) } + public fun not_found(r: u64): u64 { canonical(NOT_FOUND, r) } + public fun aborted(r: u64): u64 { canonical(ABORTED, r) } + public fun already_exists(r: u64): u64 { canonical(ALREADY_EXISTS, r) } + public fun resource_exhausted(r: u64): u64 { canonical(RESOURCE_EXHAUSTED, r) } + public fun internal(r: u64): u64 { canonical(INTERNAL, r) } + public fun not_implemented(r: u64): u64 { canonical(NOT_IMPLEMENTED, r) } + public fun unavailable(r: u64): u64 { canonical(UNAVAILABLE, r) } +} diff --git a/third_party/move/tools/move-decompiler/tests/fixed_point32.exp b/third_party/move/tools/move-decompiler/tests/fixed_point32.exp new file mode 100644 index 00000000000000..29e4301ff67322 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/fixed_point32.exp @@ -0,0 +1,94 @@ + +module 0x1::fixed_point32 { + struct FixedPoint32 has copy, drop, store { + value: u64, + } + public fun ceil(self: FixedPoint32): u64 { + let _t1 = floor(self) << 32u8; + loop { + if (!(*&(&self).value == _t1)) break; + return _t1 >> 32u8 + }; + (_t1 as u128) + 4294967296u128 >> 32u8 as u64 + } + public fun floor(self: FixedPoint32): u64 { + *&(&self).value >> 32u8 + } + public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32 { + let _t5; + let _t2 = (numerator as u128) << 64u8; + let _t3 = (denominator as u128) << 32u8; + loop { + if (_t3 != 0u128) break; + abort 65537 + }; + let _t2 = _t2 / _t3; + if (_t2 != 0u128) _t5 = true + else _t5 = numerator == 0; + loop { + if (_t5) break; + abort 131077 + }; + loop { + if (_t2 <= 18446744073709551615u128) break; + abort 131077 + }; + FixedPoint32{value: _t2 as u64} + } + public fun create_from_raw_value(value: u64): FixedPoint32 { + FixedPoint32{value: value} + } + public fun create_from_u64(val: u64): FixedPoint32 { + let _t1 = (val as u128) << 32u8; + loop { + if (_t1 <= 18446744073709551615u128) break; + abort 131077 + }; + FixedPoint32{value: _t1 as u64} + } + public fun divide_u64(val: u64, divisor: FixedPoint32): u64 { + loop { + if (*&(&divisor).value != 0) break; + abort 65540 + }; + let _t2 = ((val as u128) << 32u8) / (*&(&divisor).value as u128); + loop { + if (_t2 <= 18446744073709551615u128) break; + abort 131074 + }; + _t2 as u64 + } + public fun get_raw_value(self: FixedPoint32): u64 { + *&(&self).value + } + public fun is_zero(self: FixedPoint32): bool { + *&(&self).value == 0 + } + public fun max(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { + let _t2; + if (*&(&num1).value > *&(&num2).value) _t2 = num1 + else _t2 = num2; + _t2 + } + public fun min(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { + let _t2; + if (*&(&num1).value < *&(&num2).value) _t2 = num1 + else _t2 = num2; + _t2 + } + public fun multiply_u64(val: u64, multiplier: FixedPoint32): u64 { + let _t2 = (val as u128) * (*&(&multiplier).value as u128) >> 32u8; + loop { + if (_t2 <= 18446744073709551615u128) break; + abort 131075 + }; + _t2 as u64 + } + public fun round(self: FixedPoint32): u64 { + let _t1 = floor(self) << 32u8; + let _t2 = _t1 + 2147483648; + if (*&(&self).value < _t2) _t2 = _t1 >> 32u8 + else _t2 = ceil(self); + _t2 + } +} diff --git a/third_party/move/tools/move-decompiler/tests/fixed_point32.move b/third_party/move/tools/move-decompiler/tests/fixed_point32.move new file mode 100644 index 00000000000000..82611cb4b39b09 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/fixed_point32.move @@ -0,0 +1,295 @@ +/// Defines a fixed-point numeric type with a 32-bit integer part and +/// a 32-bit fractional part. + +module std::fixed_point32 { + + /// Define a fixed-point numeric type with 32 fractional bits. + /// This is just a u64 integer but it is wrapped in a struct to + /// make a unique type. This is a binary representation, so decimal + /// values may not be exactly representable, but it provides more + /// than 9 decimal digits of precision both before and after the + /// decimal point (18 digits total). For comparison, double precision + /// floating-point has less than 16 decimal digits of precision, so + /// be careful about using floating-point to convert these values to + /// decimal. + struct FixedPoint32 has copy, drop, store { value: u64 } + + const MAX_U64: u128 = 18446744073709551615; + + /// The denominator provided was zero + const EDENOMINATOR: u64 = 0x10001; + /// The quotient value would be too large to be held in a `u64` + const EDIVISION: u64 = 0x20002; + /// The multiplied value would be too large to be held in a `u64` + const EMULTIPLICATION: u64 = 0x20003; + /// A division by zero was encountered + const EDIVISION_BY_ZERO: u64 = 0x10004; + /// The computed ratio when converting to a `FixedPoint32` would be unrepresentable + const ERATIO_OUT_OF_RANGE: u64 = 0x20005; + + /// Multiply a u64 integer by a fixed-point number, truncating any + /// fractional part of the product. This will abort if the product + /// overflows. + public fun multiply_u64(val: u64, multiplier: FixedPoint32): u64 { + // The product of two 64 bit values has 128 bits, so perform the + // multiplication with u128 types and keep the full 128 bit product + // to avoid losing accuracy. + let unscaled_product = (val as u128) * (multiplier.value as u128); + // The unscaled product has 32 fractional bits (from the multiplier) + // so rescale it by shifting away the low bits. + let product = unscaled_product >> 32; + // Check whether the value is too large. + assert!(product <= MAX_U64, EMULTIPLICATION); + (product as u64) + } + spec multiply_u64 { + pragma opaque; + include MultiplyAbortsIf; + ensures result == spec_multiply_u64(val, multiplier); + } + spec schema MultiplyAbortsIf { + val: num; + multiplier: FixedPoint32; + aborts_if spec_multiply_u64(val, multiplier) > MAX_U64 with EMULTIPLICATION; + } + spec fun spec_multiply_u64(val: num, multiplier: FixedPoint32): num { + (val * multiplier.value) >> 32 + } + + /// Divide a u64 integer by a fixed-point number, truncating any + /// fractional part of the quotient. This will abort if the divisor + /// is zero or if the quotient overflows. + public fun divide_u64(val: u64, divisor: FixedPoint32): u64 { + // Check for division by zero. + assert!(divisor.value != 0, EDIVISION_BY_ZERO); + // First convert to 128 bits and then shift left to + // add 32 fractional zero bits to the dividend. + let scaled_value = (val as u128) << 32; + let quotient = scaled_value / (divisor.value as u128); + // Check whether the value is too large. + assert!(quotient <= MAX_U64, EDIVISION); + // the value may be too large, which will cause the cast to fail + // with an arithmetic error. + (quotient as u64) + } + spec divide_u64 { + pragma opaque; + include DivideAbortsIf; + ensures result == spec_divide_u64(val, divisor); + } + spec schema DivideAbortsIf { + val: num; + divisor: FixedPoint32; + aborts_if divisor.value == 0 with EDIVISION_BY_ZERO; + aborts_if spec_divide_u64(val, divisor) > MAX_U64 with EDIVISION; + } + spec fun spec_divide_u64(val: num, divisor: FixedPoint32): num { + (val << 32) / divisor.value + } + + /// Create a fixed-point value from a rational number specified by its + /// numerator and denominator. Calling this function should be preferred + /// for using `Self::create_from_raw_value` which is also available. + /// This will abort if the denominator is zero. It will also + /// abort if the numerator is nonzero and the ratio is not in the range + /// 2^-32 .. 2^32-1. When specifying decimal fractions, be careful about + /// rounding errors: if you round to display N digits after the decimal + /// point, you can use a denominator of 10^N to avoid numbers where the + /// very small imprecision in the binary representation could change the + /// rounding, e.g., 0.0125 will round down to 0.012 instead of up to 0.013. + public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32 { + // If the denominator is zero, this will abort. + // Scale the numerator to have 64 fractional bits and the denominator + // to have 32 fractional bits, so that the quotient will have 32 + // fractional bits. + let scaled_numerator = (numerator as u128) << 64; + let scaled_denominator = (denominator as u128) << 32; + assert!(scaled_denominator != 0, EDENOMINATOR); + let quotient = scaled_numerator / scaled_denominator; + assert!(quotient != 0 || numerator == 0, ERATIO_OUT_OF_RANGE); + // Return the quotient as a fixed-point number. We first need to check whether the cast + // can succeed. + assert!(quotient <= MAX_U64, ERATIO_OUT_OF_RANGE); + FixedPoint32 { value: (quotient as u64) } + } + spec create_from_rational { + pragma opaque; + include CreateFromRationalAbortsIf; + ensures result == spec_create_from_rational(numerator, denominator); + } + spec schema CreateFromRationalAbortsIf { + numerator: u64; + denominator: u64; + let scaled_numerator = (numerator as u128)<< 64; + let scaled_denominator = (denominator as u128) << 32; + let quotient = scaled_numerator / scaled_denominator; + aborts_if scaled_denominator == 0 with EDENOMINATOR; + aborts_if quotient == 0 && scaled_numerator != 0 with ERATIO_OUT_OF_RANGE; + aborts_if quotient > MAX_U64 with ERATIO_OUT_OF_RANGE; + } + spec fun spec_create_from_rational(numerator: num, denominator: num): FixedPoint32 { + FixedPoint32{value: (numerator << 64) / (denominator << 32)} + } + + /// Create a fixedpoint value from a raw value. + public fun create_from_raw_value(value: u64): FixedPoint32 { + FixedPoint32 { value } + } + spec create_from_raw_value { + pragma opaque; + aborts_if false; + ensures result.value == value; + } + + /// Accessor for the raw u64 value. Other less common operations, such as + /// adding or subtracting FixedPoint32 values, can be done using the raw + /// values directly. + public fun get_raw_value(self: FixedPoint32): u64 { + self.value + } + + /// Returns true if the ratio is zero. + public fun is_zero(self: FixedPoint32): bool { + self.value == 0 + } + + /// Returns the smaller of the two FixedPoint32 numbers. + public fun min(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { + if (num1.value < num2.value) { + num1 + } else { + num2 + } + } + spec min { + pragma opaque; + aborts_if false; + ensures result == spec_min(num1, num2); + } + spec fun spec_min(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { + if (num1.value < num2.value) { + num1 + } else { + num2 + } + } + + /// Returns the larger of the two FixedPoint32 numbers. + public fun max(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { + if (num1.value > num2.value) { + num1 + } else { + num2 + } + } + spec max { + pragma opaque; + aborts_if false; + ensures result == spec_max(num1, num2); + } + spec fun spec_max(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { + if (num1.value > num2.value) { + num1 + } else { + num2 + } + } + + /// Create a fixedpoint value from a u64 value. + public fun create_from_u64(val: u64): FixedPoint32 { + let value = (val as u128) << 32; + assert!(value <= MAX_U64, ERATIO_OUT_OF_RANGE); + FixedPoint32 {value: (value as u64)} + } + spec create_from_u64 { + pragma opaque; + include CreateFromU64AbortsIf; + ensures result == spec_create_from_u64(val); + } + spec schema CreateFromU64AbortsIf { + val: num; + let scaled_value = (val as u128) << 32; + aborts_if scaled_value > MAX_U64; + } + spec fun spec_create_from_u64(val: num): FixedPoint32 { + FixedPoint32 {value: val << 32} + } + + /// Returns the largest integer less than or equal to a given number. + public fun floor(self: FixedPoint32): u64 { + self.value >> 32 + } + spec floor { + pragma opaque; + aborts_if false; + ensures result == spec_floor(self); + } + spec fun spec_floor(self: FixedPoint32): u64 { + let fractional = self.value % (1 << 32); + if (fractional == 0) { + self.value >> 32 + } else { + (self.value - fractional) >> 32 + } + } + + /// Rounds up the given FixedPoint32 to the next largest integer. + public fun ceil(self: FixedPoint32): u64 { + let floored_num = floor(self) << 32; + if (self.value == floored_num) { + return floored_num >> 32 + }; + let val = ((floored_num as u128) + (1 << 32)); + (val >> 32 as u64) + } + spec ceil { + pragma verify_duration_estimate = 120; + pragma opaque; + aborts_if false; + ensures result == spec_ceil(self); + } + spec fun spec_ceil(self: FixedPoint32): u64 { + let fractional = self.value % (1 << 32); + let one = 1 << 32; + if (fractional == 0) { + self.value >> 32 + } else { + (self.value - fractional + one) >> 32 + } + } + + /// Returns the value of a FixedPoint32 to the nearest integer. + public fun round(self: FixedPoint32): u64 { + let floored_num = floor(self) << 32; + let boundary = floored_num + ((1 << 32) / 2); + if (self.value < boundary) { + floored_num >> 32 + } else { + ceil(self) + } + } + spec round { + pragma verify_duration_estimate = 120; + pragma opaque; + aborts_if false; + ensures result == spec_round(self); + } + spec fun spec_round(self: FixedPoint32): u64 { + let fractional = self.value % (1 << 32); + let boundary = (1 << 32) / 2; + let one = 1 << 32; + if (fractional < boundary) { + (self.value - fractional) >> 32 + } else { + (self.value - fractional + one) >> 32 + } + } + + // **************** SPECIFICATIONS **************** + + spec module {} // switch documentation context to module level + + spec module { + pragma aborts_if_is_strict; + } +} diff --git a/third_party/move/tools/move-decompiler/tests/option.exp b/third_party/move/tools/move-decompiler/tests/option.exp new file mode 100644 index 00000000000000..1959875d600899 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/option.exp @@ -0,0 +1,119 @@ + +module 0x1::option { + use 0x1::vector; + struct Option has copy, drop, store { + vec: vector, + } + public fun borrow(self: &Option): &Element { + loop { + if (is_some(self)) break; + abort 262145 + }; + vector::borrow(&self.vec, 0) + } + public fun borrow_mut(self: &mut Option): &mut Element { + loop { + if (is_some(/*freeze*/self)) break; + abort 262145 + }; + vector::borrow_mut(&mut self.vec, 0) + } + public fun swap(self: &mut Option, e: Element): Element { + loop { + if (is_some(/*freeze*/self)) break; + abort 262145 + }; + let _t2 = &mut self.vec; + vector::push_back(_t2, e); + vector::pop_back(_t2) + } + public fun contains(self: &Option, e_ref: &Element): bool { + vector::contains(&self.vec, e_ref) + } + public fun is_some(self: &Option): bool { + !vector::is_empty(&self.vec) + } + public fun borrow_with_default(self: &Option, default_ref: &Element): &Element { + let _t3; + let _t2 = &self.vec; + if (vector::is_empty(_t2)) _t3 = default_ref + else _t3 = vector::borrow(_t2, 0); + _t3 + } + public fun destroy_none(self: Option) { + loop { + if (is_none(&self)) break; + abort 262144 + }; + let Option{vec: _t5} = self; + vector::destroy_empty(_t5); + } + public fun is_none(self: &Option): bool { + vector::is_empty(&self.vec) + } + public fun destroy_some(self: Option): Element { + loop { + if (is_some(&self)) break; + abort 262145 + }; + let Option{vec: _t6} = self; + let _t1 = _t6; + vector::destroy_empty(_t1); + vector::pop_back(&mut _t1) + } + public fun destroy_with_default(self: Option, default: Element): Element { + let _t3; + let Option{vec: _t5} = self; + let _t2 = _t5; + if (vector::is_empty(/*freeze*/&mut _t2)) _t3 = default + else _t3 = vector::pop_back(&mut _t2); + _t3 + } + public fun extract(self: &mut Option): Element { + loop { + if (is_some(/*freeze*/self)) break; + abort 262145 + }; + vector::pop_back(&mut self.vec) + } + public fun fill(self: &mut Option, e: Element) { + let _t2 = &mut self.vec; + loop { + if (vector::is_empty(/*freeze*/_t2)) break; + abort 262144 + }; + vector::push_back(_t2, e); + } + public fun from_vec(vec: vector): Option { + loop { + if (vector::length(&vec) <= 1) break; + abort 262146 + }; + Option{vec: vec} + } + public fun get_with_default(self: &Option, default: Element): Element { + let _t3; + let _t2 = &self.vec; + if (vector::is_empty(_t2)) _t3 = default + else _t3 = *vector::borrow(_t2, 0); + _t3 + } + public fun none(): Option { + Option{vec: vector::empty()} + } + public fun some(e: Element): Option { + Option{vec: vector::singleton(e)} + } + public fun swap_or_fill(self: &mut Option, e: Element): Option { + let _t3; + let _t2 = &mut self.vec; + if (vector::is_empty(/*freeze*/_t2)) _t3 = none() + else _t3 = some(vector::pop_back(_t2)); + vector::push_back(_t2, e); + _t3 + } + public fun to_vec(self: Option): vector { + let Option{vec: _t2} = self; + _t2 + } +} diff --git a/third_party/move/tools/move-decompiler/tests/option.move b/third_party/move/tools/move-decompiler/tests/option.move new file mode 100644 index 00000000000000..50aefbe12d008b --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/option.move @@ -0,0 +1,356 @@ +/// This module defines the Option type and its methods to represent and handle an optional value. +module std::option { + use std::vector; + + /// Abstraction of a value that may or may not be present. Implemented with a vector of size + /// zero or one because Move bytecode does not have ADTs. + struct Option has copy, drop, store { + vec: vector + } + spec Option { + /// The size of vector is always less than equal to 1 + /// because it's 0 for "none" or 1 for "some". + invariant len(vec) <= 1; + } + + /// The `Option` is in an invalid state for the operation attempted. + /// The `Option` is `Some` while it should be `None`. + const EOPTION_IS_SET: u64 = 0x40000; + /// The `Option` is in an invalid state for the operation attempted. + /// The `Option` is `None` while it should be `Some`. + const EOPTION_NOT_SET: u64 = 0x40001; + /// Cannot construct an option from a vector with 2 or more elements. + const EOPTION_VEC_TOO_LONG: u64 = 0x40002; + + /// Return an empty `Option` + public fun none(): Option { + Option { vec: vector::empty() } + } + spec none { + pragma opaque; + aborts_if false; + ensures result == spec_none(); + } + spec fun spec_none(): Option { + Option{ vec: vec() } + } + + /// Return an `Option` containing `e` + public fun some(e: Element): Option { + Option { vec: vector::singleton(e) } + } + spec some { + pragma opaque; + aborts_if false; + ensures result == spec_some(e); + } + spec fun spec_some(e: Element): Option { + Option{ vec: vec(e) } + } + + public fun from_vec(vec: vector): Option { + assert!(vector::length(&vec) <= 1, EOPTION_VEC_TOO_LONG); + Option { vec } + } + + spec from_vec { + aborts_if vector::length(vec) > 1; + } + + /// Return true if `self` does not hold a value + public fun is_none(self: &Option): bool { + vector::is_empty(&self.vec) + } + spec is_none { + pragma opaque; + aborts_if false; + ensures result == spec_is_none(self); + } + spec fun spec_is_none(self: Option): bool { + vector::is_empty(self.vec) + } + + /// Return true if `self` holds a value + public fun is_some(self: &Option): bool { + !vector::is_empty(&self.vec) + } + spec is_some { + pragma opaque; + aborts_if false; + ensures result == spec_is_some(self); + } + spec fun spec_is_some(self: Option): bool { + !vector::is_empty(self.vec) + } + + /// Return true if the value in `self` is equal to `e_ref` + /// Always returns `false` if `self` does not hold a value + public fun contains(self: &Option, e_ref: &Element): bool { + vector::contains(&self.vec, e_ref) + } + spec contains { + pragma opaque; + aborts_if false; + ensures result == spec_contains(self, e_ref); + } + spec fun spec_contains(self: Option, e: Element): bool { + is_some(self) && borrow(self) == e + } + + /// Return an immutable reference to the value inside `self` + /// Aborts if `self` does not hold a value + public fun borrow(self: &Option): &Element { + assert!(is_some(self), EOPTION_NOT_SET); + vector::borrow(&self.vec, 0) + } + spec borrow { + pragma opaque; + include AbortsIfNone; + ensures result == spec_borrow(self); + } + spec fun spec_borrow(self: Option): Element { + self.vec[0] + } + + /// Return a reference to the value inside `self` if it holds one + /// Return `default_ref` if `self` does not hold a value + public fun borrow_with_default(self: &Option, default_ref: &Element): &Element { + let vec_ref = &self.vec; + if (vector::is_empty(vec_ref)) default_ref + else vector::borrow(vec_ref, 0) + } + spec borrow_with_default { + pragma opaque; + aborts_if false; + ensures result == (if (spec_is_some(self)) spec_borrow(self) else default_ref); + } + + /// Return the value inside `self` if it holds one + /// Return `default` if `self` does not hold a value + public fun get_with_default( + self: &Option, + default: Element, + ): Element { + let vec_ref = &self.vec; + if (vector::is_empty(vec_ref)) default + else *vector::borrow(vec_ref, 0) + } + spec get_with_default { + pragma opaque; + aborts_if false; + ensures result == (if (spec_is_some(self)) spec_borrow(self) else default); + } + + /// Convert the none option `self` to a some option by adding `e`. + /// Aborts if `self` already holds a value + public fun fill(self: &mut Option, e: Element) { + let vec_ref = &mut self.vec; + if (vector::is_empty(vec_ref)) vector::push_back(vec_ref, e) + else abort EOPTION_IS_SET + } + spec fill { + pragma opaque; + aborts_if spec_is_some(self) with EOPTION_IS_SET; + ensures spec_is_some(self); + ensures spec_borrow(self) == e; + } + + /// Convert a `some` option to a `none` by removing and returning the value stored inside `self` + /// Aborts if `self` does not hold a value + public fun extract(self: &mut Option): Element { + assert!(is_some(self), EOPTION_NOT_SET); + vector::pop_back(&mut self.vec) + } + spec extract { + pragma opaque; + include AbortsIfNone; + ensures result == spec_borrow(old(self)); + ensures spec_is_none(self); + } + + /// Return a mutable reference to the value inside `self` + /// Aborts if `self` does not hold a value + public fun borrow_mut(self: &mut Option): &mut Element { + assert!(is_some(self), EOPTION_NOT_SET); + vector::borrow_mut(&mut self.vec, 0) + } + spec borrow_mut { + include AbortsIfNone; + ensures result == spec_borrow(self); + ensures self == old(self); + } + + /// Swap the old value inside `self` with `e` and return the old value + /// Aborts if `self` does not hold a value + public fun swap(self: &mut Option, e: Element): Element { + assert!(is_some(self), EOPTION_NOT_SET); + let vec_ref = &mut self.vec; + let old_value = vector::pop_back(vec_ref); + vector::push_back(vec_ref, e); + old_value + } + spec swap { + pragma opaque; + include AbortsIfNone; + ensures result == spec_borrow(old(self)); + ensures spec_is_some(self); + ensures spec_borrow(self) == e; + } + + /// Swap the old value inside `self` with `e` and return the old value; + /// or if there is no old value, fill it with `e`. + /// Different from swap(), swap_or_fill() allows for `self` not holding a value. + public fun swap_or_fill(self: &mut Option, e: Element): Option { + let vec_ref = &mut self.vec; + let old_value = if (vector::is_empty(vec_ref)) none() + else some(vector::pop_back(vec_ref)); + vector::push_back(vec_ref, e); + old_value + } + spec swap_or_fill { + pragma opaque; + aborts_if false; + ensures result == old(self); + ensures spec_borrow(self) == e; + } + + /// Destroys `self.` If `self` holds a value, return it. Returns `default` otherwise + public fun destroy_with_default(self: Option, default: Element): Element { + let Option { vec } = self; + if (vector::is_empty(&mut vec)) default + else vector::pop_back(&mut vec) + } + spec destroy_with_default { + pragma opaque; + aborts_if false; + ensures result == (if (spec_is_some(self)) spec_borrow(self) else default); + } + + /// Unpack `self` and return its contents + /// Aborts if `self` does not hold a value + public fun destroy_some(self: Option): Element { + assert!(is_some(&self), EOPTION_NOT_SET); + let Option { vec } = self; + let elem = vector::pop_back(&mut vec); + vector::destroy_empty(vec); + elem + } + spec destroy_some { + pragma opaque; + include AbortsIfNone; + ensures result == spec_borrow(self); + } + + /// Unpack `self` + /// Aborts if `self` holds a value + public fun destroy_none(self: Option) { + assert!(is_none(&self), EOPTION_IS_SET); + let Option { vec } = self; + vector::destroy_empty(vec) + } + spec destroy_none { + pragma opaque; + aborts_if spec_is_some(self) with EOPTION_IS_SET; + } + + /// Convert `self` into a vector of length 1 if it is `Some`, + /// and an empty vector otherwise + public fun to_vec(self: Option): vector { + let Option { vec } = self; + vec + } + spec to_vec { + pragma opaque; + aborts_if false; + ensures result == self.vec; + } + /// Apply the function to the optional element, consuming it. Does nothing if no value present. + public inline fun for_each(self: Option, f: |Element|) { + if (is_some(&self)) { + f(destroy_some(self)) + } else { + destroy_none(self) + } + } + + /// Apply the function to the optional element reference. Does nothing if no value present. + public inline fun for_each_ref(self: &Option, f: |&Element|) { + if (is_some(self)) { + f(borrow(self)) + } + } + + /// Apply the function to the optional element reference. Does nothing if no value present. + public inline fun for_each_mut(self: &mut Option, f: |&mut Element|) { + if (is_some(self)) { + f(borrow_mut(self)) + } + } + + /// Folds the function over the optional element. + public inline fun fold( + self: Option, + init: Accumulator, + f: |Accumulator,Element|Accumulator + ): Accumulator { + if (is_some(&self)) { + f(init, destroy_some(self)) + } else { + destroy_none(self); + init + } + } + + /// Maps the content of an option. + public inline fun map(self: Option, f: |Element|OtherElement): Option { + if (is_some(&self)) { + some(f(destroy_some(self))) + } else { + destroy_none(self); + none() + } + } + + /// Maps the content of an option without destroying the original option. + public inline fun map_ref( + self: &Option, f: |&Element|OtherElement): Option { + if (is_some(self)) { + some(f(borrow(self))) + } else { + none() + } + } + + /// Filters the content of an option + public inline fun filter(self: Option, f: |&Element|bool): Option { + if (is_some(&self) && f(borrow(&self))) { + self + } else { + none() + } + } + + /// Returns true if the option contains an element which satisfies predicate. + public inline fun any(self: &Option, p: |&Element|bool): bool { + is_some(self) && p(borrow(self)) + } + + /// Utility function to destroy an option that is not droppable. + public inline fun destroy(self: Option, d: |Element|) { + let vec = to_vec(self); + vector::destroy(vec, |e| d(e)); + } + + spec module {} // switch documentation context back to module level + + spec module { + pragma aborts_if_is_strict; + } + + /// # Helper Schema + + spec schema AbortsIfNone { + self: Option; + aborts_if spec_is_none(self) with EOPTION_NOT_SET; + } +} diff --git a/third_party/move/tools/move-decompiler/tests/simple_map.exp b/third_party/move/tools/move-decompiler/tests/simple_map.exp new file mode 100644 index 00000000000000..066eaae9f05f3f --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/simple_map.exp @@ -0,0 +1,160 @@ + +module 0x1::simple_map { + use 0x1::option; + use 0x1::error; + use 0x1::vector; + struct Element has copy, drop, store { + key: Key, + value: Value, + } + struct SimpleMap has copy, drop, store { + data: vector>, + } + public fun length(self: &SimpleMap): u64 { + vector::length>(&self.data) + } + public fun borrow(self: &SimpleMap, key: &Key): &Value { + let _t2 = find(self, key); + loop { + if (option::is_some(&_t2)) break; + abort error::invalid_argument(2) + }; + &vector::borrow>(&self.data, option::extract(&mut _t2)).value + } + public fun borrow_mut(self: &mut SimpleMap, key: &Key): &mut Value { + let _t2 = find(/*freeze*/self, key); + loop { + if (option::is_some(&_t2)) break; + abort error::invalid_argument(2) + }; + &mut vector::borrow_mut>(&mut self.data, option::extract(&mut _t2)).value + } + public fun destroy_empty(self: SimpleMap) { + let SimpleMap{data: _t2} = self; + vector::destroy_empty>(_t2); + } + fun find(self: &SimpleMap, key: &Key): option::Option { + let _t3 = 0; + 'l0: loop { + loop { + if (!(_t3 < vector::length>(&self.data))) break; + if (!(&vector::borrow>(&self.data, _t3).key == key)) { + _t3 = _t3 + 1; + continue 'l0 + }; + return option::some(_t3) + }; + break + }; + option::none() + } + public fun remove(self: &mut SimpleMap, key: &Key): (Key, Value) { + let _t2 = find(/*freeze*/self, key); + loop { + if (option::is_some(&_t2)) break; + abort error::invalid_argument(2) + }; + let Element{key: _t19,value: _t20} = vector::swap_remove>(&mut self.data, option::extract(&mut _t2)); + (_t19, _t20) + } + public fun add(self: &mut SimpleMap, key: Key, value: Value) { + loop { + if (option::is_none(&find(/*freeze*/self, &key))) break; + abort error::invalid_argument(1) + }; + vector::push_back>(&mut self.data, Element{key: key,value: value}); + } + public fun add_all(self: &mut SimpleMap, keys: vector, values: vector) { + let _t3 = keys; + let _t4 = values; + vector::reverse(&mut _t3); + vector::reverse(&mut _t4); + let _t5 = _t3; + let _t6 = _t4; + if (!(vector::length(&_t5) == vector::length(&_t6))) { + abort 131074; + while (vector::length(&_t5) > 0) { + add(self, vector::pop_back(&mut _t5), vector::pop_back(&mut _t6)); + let _t7 = vector::length(&_t5) - 1; + continue + } + }; + vector::destroy_empty(_t5); + vector::destroy_empty(_t6); + } + public fun keys(self: &SimpleMap): vector { + let _t1 = &self.data; + let _t2 = vector::empty(); + let _t3 = 0; + while (_t3 < vector::length>(_t1)) { + vector::push_back(&mut _t2, *&vector::borrow>(_t1, _t3).key); + let _t3 = _t3 + 1; + continue + }; + _t2 + } + public fun values(self: &SimpleMap): vector { + let _t1 = &self.data; + let _t2 = vector::empty(); + let _t3 = 0; + while (_t3 < vector::length>(_t1)) { + vector::push_back(&mut _t2, *&vector::borrow>(_t1, _t3).value); + let _t3 = _t3 + 1; + continue + }; + _t2 + } + public fun contains_key(self: &SimpleMap, key: &Key): bool { + option::is_some(&find(self, key)) + } + public fun create(): SimpleMap { + new() + } + public fun new(): SimpleMap { + SimpleMap{data: vector::empty>()} + } + public fun new_from(keys: vector, values: vector): SimpleMap { + let _t2 = new(); + add_all(&mut _t2, keys, values); + _t2 + } + public fun to_vec_pair(self: SimpleMap): (vector, vector) { + let _t1 = vector::empty(); + let _t2 = vector::empty(); + let SimpleMap{data: _t14} = self; + let _t3 = _t14; + vector::reverse>(&mut _t3); + let _t4 = _t3; + let _t5 = vector::length>(&_t4); + while (_t5 > 0) { + let Element{key: _t24,value: _t25} = vector::pop_back>(&mut _t4); + vector::push_back(&mut _t1, _t24); + vector::push_back(&mut _t2, _t25); + let _t5 = _t5 - 1; + continue + }; + vector::destroy_empty>(_t4); + (_t1, _t2) + } + public fun upsert(self: &mut SimpleMap, key: Key, value: Value): (option::Option, option::Option) { + let _t3 = &mut self.data; + let _t4 = vector::length>(/*freeze*/_t3); + let _t5 = 0; + 'l0: loop { + loop { + if (!(_t5 < _t4)) break; + if (!(&vector::borrow>(/*freeze*/_t3, _t5).key == &key)) { + _t5 = _t5 + 1; + continue 'l0 + }; + vector::push_back>(_t3, Element{key: key,value: value}); + vector::swap>(_t3, _t5, _t4); + let Element{key: _t37,value: _t38} = vector::pop_back>(_t3); + return (option::some(_t37), option::some(_t38)) + }; + break + }; + vector::push_back>(&mut self.data, Element{key: key,value: value}); + (option::none(), option::none()) + } +} diff --git a/third_party/move/tools/move-decompiler/tests/simple_map.move b/third_party/move/tools/move-decompiler/tests/simple_map.move new file mode 100644 index 00000000000000..d672065b1467d1 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/simple_map.move @@ -0,0 +1,319 @@ +/// This module provides a solution for unsorted maps, that is it has the properties that +/// 1) Keys point to Values +/// 2) Each Key must be unique +/// 3) A Key can be found within O(N) time +/// 4) The keys are unsorted. +/// 5) Adds and removals take O(N) time +module aptos_std::simple_map { + use std::error; + use std::option; + use std::vector; + + /// Map key already exists + const EKEY_ALREADY_EXISTS: u64 = 1; + /// Map key is not found + const EKEY_NOT_FOUND: u64 = 2; + + struct SimpleMap has copy, drop, store { + data: vector>, + } + + struct Element has copy, drop, store { + key: Key, + value: Value, + } + + public fun length(self: &SimpleMap): u64 { + vector::length(&self.data) + } + + /// Create an empty SimpleMap. + public fun new(): SimpleMap { + SimpleMap { + data: vector::empty(), + } + } + + /// Create a SimpleMap from a vector of keys and values. The keys must be unique. + public fun new_from( + keys: vector, + values: vector, + ): SimpleMap { + let map = new(); + add_all(&mut map, keys, values); + map + } + + #[deprecated] + /// Create an empty SimpleMap. + /// This function is deprecated, use `new` instead. + public fun create(): SimpleMap { + new() + } + + public fun borrow( + self: &SimpleMap, + key: &Key, + ): &Value { + let maybe_idx = find(self, key); + assert!(option::is_some(&maybe_idx), error::invalid_argument(EKEY_NOT_FOUND)); + let idx = option::extract(&mut maybe_idx); + &vector::borrow(&self.data, idx).value + } + + public fun borrow_mut( + self: &mut SimpleMap, + key: &Key, + ): &mut Value { + let maybe_idx = find(self, key); + assert!(option::is_some(&maybe_idx), error::invalid_argument(EKEY_NOT_FOUND)); + let idx = option::extract(&mut maybe_idx); + &mut vector::borrow_mut(&mut self.data, idx).value + } + + public fun contains_key( + self: &SimpleMap, + key: &Key, + ): bool { + let maybe_idx = find(self, key); + option::is_some(&maybe_idx) + } + + public fun destroy_empty(self: SimpleMap) { + let SimpleMap { data } = self; + vector::destroy_empty(data); + } + + /// Add a key/value pair to the map. The key must not already exist. + public fun add( + self: &mut SimpleMap, + key: Key, + value: Value, + ) { + let maybe_idx = find(self, &key); + assert!(option::is_none(&maybe_idx), error::invalid_argument(EKEY_ALREADY_EXISTS)); + + vector::push_back(&mut self.data, Element { key, value }); + } + + /// Add multiple key/value pairs to the map. The keys must not already exist. + public fun add_all( + self: &mut SimpleMap, + keys: vector, + values: vector, + ) { + vector::zip(keys, values, |key, value| { + add(self, key, value); + }); + } + + /// Insert key/value pair or update an existing key to a new value + public fun upsert( + self: &mut SimpleMap, + key: Key, + value: Value + ): (std::option::Option, std::option::Option) { + let data = &mut self.data; + let len = vector::length(data); + let i = 0; + while (i < len) { + let element = vector::borrow(data, i); + if (&element.key == &key) { + vector::push_back(data, Element { key, value }); + vector::swap(data, i, len); + let Element { key, value } = vector::pop_back(data); + return (std::option::some(key), std::option::some(value)) + }; + i = i + 1; + }; + vector::push_back(&mut self.data, Element { key, value }); + (std::option::none(), std::option::none()) + } + + /// Return all keys in the map. This requires keys to be copyable. + public fun keys(self: &SimpleMap): vector { + vector::map_ref(&self.data, |e| { + let e: &Element = e; + e.key + }) + } + + /// Return all values in the map. This requires values to be copyable. + public fun values(self: &SimpleMap): vector { + vector::map_ref(&self.data, |e| { + let e: &Element = e; + e.value + }) + } + + /// Transform the map into two vectors with the keys and values respectively + /// Primarily used to destroy a map + public fun to_vec_pair( + self: SimpleMap): (vector, vector) { + let keys: vector = vector::empty(); + let values: vector = vector::empty(); + let SimpleMap { data } = self; + vector::for_each(data, |e| { + let Element { key, value } = e; + vector::push_back(&mut keys, key); + vector::push_back(&mut values, value); + }); + (keys, values) + } + + /// For maps that cannot be dropped this is a utility to destroy them + /// using lambdas to destroy the individual keys and values. + public inline fun destroy( + self: SimpleMap, + dk: |Key|, + dv: |Value| + ) { + let (keys, values) = to_vec_pair(self); + vector::destroy(keys, |_k| dk(_k)); + vector::destroy(values, |_v| dv(_v)); + } + + /// Remove a key/value pair from the map. The key must exist. + public fun remove( + self: &mut SimpleMap, + key: &Key, + ): (Key, Value) { + let maybe_idx = find(self, key); + assert!(option::is_some(&maybe_idx), error::invalid_argument(EKEY_NOT_FOUND)); + let placement = option::extract(&mut maybe_idx); + let Element { key, value } = vector::swap_remove(&mut self.data, placement); + (key, value) + } + + fun find( + self: &SimpleMap, + key: &Key, + ): option::Option { + let leng = vector::length(&self.data); + let i = 0; + while (i < leng) { + let element = vector::borrow(&self.data, i); + if (&element.key == key) { + return option::some(i) + }; + i = i + 1; + }; + option::none() + } + + #[test] + public fun test_add_remove_many() { + let map = create(); + + assert!(length(&map) == 0, 0); + assert!(!contains_key(&map, &3), 1); + add(&mut map, 3, 1); + assert!(length(&map) == 1, 2); + assert!(contains_key(&map, &3), 3); + assert!(borrow(&map, &3) == &1, 4); + *borrow_mut(&mut map, &3) = 2; + assert!(borrow(&map, &3) == &2, 5); + + assert!(!contains_key(&map, &2), 6); + add(&mut map, 2, 5); + assert!(length(&map) == 2, 7); + assert!(contains_key(&map, &2), 8); + assert!(borrow(&map, &2) == &5, 9); + *borrow_mut(&mut map, &2) = 9; + assert!(borrow(&map, &2) == &9, 10); + + remove(&mut map, &2); + assert!(length(&map) == 1, 11); + assert!(!contains_key(&map, &2), 12); + assert!(borrow(&map, &3) == &2, 13); + + remove(&mut map, &3); + assert!(length(&map) == 0, 14); + assert!(!contains_key(&map, &3), 15); + + destroy_empty(map); + } + + #[test] + public fun test_add_all() { + let map = create(); + + assert!(length(&map) == 0, 0); + add_all(&mut map, vector[1, 2, 3], vector[10, 20, 30]); + assert!(length(&map) == 3, 1); + assert!(borrow(&map, &1) == &10, 2); + assert!(borrow(&map, &2) == &20, 3); + assert!(borrow(&map, &3) == &30, 4); + + remove(&mut map, &1); + remove(&mut map, &2); + remove(&mut map, &3); + destroy_empty(map); + } + + #[test] + public fun test_keys() { + let map = create(); + assert!(keys(&map) == vector[], 0); + add(&mut map, 2, 1); + add(&mut map, 3, 1); + + assert!(keys(&map) == vector[2, 3], 0); + } + + #[test] + public fun test_values() { + let map = create(); + assert!(values(&map) == vector[], 0); + add(&mut map, 2, 1); + add(&mut map, 3, 2); + + assert!(values(&map) == vector[1, 2], 0); + } + + #[test] + #[expected_failure] + public fun test_add_twice() { + let map = create(); + add(&mut map, 3, 1); + add(&mut map, 3, 1); + + remove(&mut map, &3); + destroy_empty(map); + } + + #[test] + #[expected_failure] + public fun test_remove_twice() { + let map = create(); + add(&mut map, 3, 1); + remove(&mut map, &3); + remove(&mut map, &3); + + destroy_empty(map); + } + + #[test] + public fun test_upsert_test() { + let map = create(); + // test adding 3 elements using upsert + upsert(&mut map, 1, 1); + upsert(&mut map, 2, 2); + upsert(&mut map, 3, 3); + + assert!(length(&map) == 3, 0); + assert!(contains_key(&map, &1), 1); + assert!(contains_key(&map, &2), 2); + assert!(contains_key(&map, &3), 3); + assert!(borrow(&map, &1) == &1, 4); + assert!(borrow(&map, &2) == &2, 5); + assert!(borrow(&map, &3) == &3, 6); + + // change mapping 1->1 to 1->4 + upsert(&mut map, 1, 4); + + assert!(length(&map) == 3, 7); + assert!(contains_key(&map, &1), 8); + assert!(borrow(&map, &1) == &4, 9); + } +} diff --git a/third_party/move/tools/move-decompiler/tests/string.exp b/third_party/move/tools/move-decompiler/tests/string.exp new file mode 100644 index 00000000000000..be51f4e1748ab3 --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/string.exp @@ -0,0 +1,73 @@ + +module 0x1::string { + use 0x1::vector; + use 0x1::option; + struct String has copy, drop, store { + bytes: vector, + } + public fun length(self: &String): u64 { + vector::length(&self.bytes) + } + public fun bytes(self: &String): &vector { + &self.bytes + } + public fun index_of(self: &String, r: &String): u64 { + internal_index_of(&self.bytes, &r.bytes) + } + native fun internal_index_of(v: &vector, r: &vector): u64; + public fun append(self: &mut String, r: String) { + vector::append(&mut self.bytes, *&(&r).bytes); + } + public fun insert(self: &mut String, at: u64, o: String) { + let _t6; + let _t3 = &self.bytes; + let _t4 = at; + if (_t4 <= vector::length(_t3)) _t6 = internal_is_char_boundary(_t3, at) + else _t6 = false; + loop { + if (_t6) break; + abort 2 + }; + let _t7 = sub_string(/*freeze*/self, 0, at); + append(&mut _t7, o); + append(&mut _t7, sub_string(/*freeze*/self, at, length(/*freeze*/self))); + *self = _t7; + } + native fun internal_is_char_boundary(v: &vector, i: u64): bool; + public fun sub_string(self: &String, i: u64, j: u64): String { + let _t5; + let _t3 = &self.bytes; + if (j <= vector::length(_t3)) _t5 = i <= j + else _t5 = false; + if (_t5) _t5 = internal_is_char_boundary(_t3, i) + else _t5 = false; + if (_t5) _t5 = internal_is_char_boundary(_t3, j) + else _t5 = false; + loop { + if (_t5) break; + abort 2 + }; + String{bytes: internal_sub_string(_t3, i, j)} + } + public fun is_empty(self: &String): bool { + vector::is_empty(&self.bytes) + } + public fun append_utf8(self: &mut String, bytes: vector) { + append(self, utf8(bytes)); + } + public fun utf8(bytes: vector): String { + loop { + if (internal_check_utf8(&bytes)) break; + abort 1 + }; + String{bytes: bytes} + } + public native fun internal_check_utf8(v: &vector): bool; + native fun internal_sub_string(v: &vector, i: u64, j: u64): vector; + public fun try_utf8(bytes: vector): option::Option { + let _t1; + if (internal_check_utf8(&bytes)) _t1 = option::some(String{bytes: bytes}) + else _t1 = option::none(); + _t1 + } +} diff --git a/third_party/move/tools/move-decompiler/tests/string.move b/third_party/move/tools/move-decompiler/tests/string.move new file mode 100644 index 00000000000000..549aa2380e36af --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/string.move @@ -0,0 +1,93 @@ +/// The `string` module defines the `String` type which represents UTF8 encoded strings. +module std::string { + use std::vector; + use std::option::{Self, Option}; + + /// An invalid UTF8 encoding. + const EINVALID_UTF8: u64 = 1; + + /// Index out of range. + const EINVALID_INDEX: u64 = 2; + + /// A `String` holds a sequence of bytes which is guaranteed to be in utf8 format. + struct String has copy, drop, store { + bytes: vector, + } + + /// Creates a new string from a sequence of bytes. Aborts if the bytes do not represent valid utf8. + public fun utf8(bytes: vector): String { + assert!(internal_check_utf8(&bytes), EINVALID_UTF8); + String{bytes} + } + + /// Tries to create a new string from a sequence of bytes. + public fun try_utf8(bytes: vector): Option { + if (internal_check_utf8(&bytes)) { + option::some(String{bytes}) + } else { + option::none() + } + } + + /// Returns a reference to the underlying byte vector. + public fun bytes(self: &String): &vector { + &self.bytes + } + + /// Checks whether this string is empty. + public fun is_empty(self: &String): bool { + vector::is_empty(&self.bytes) + } + + /// Returns the length of this string, in bytes. + public fun length(self: &String): u64 { + vector::length(&self.bytes) + } + + /// Appends a string. + public fun append(self: &mut String, r: String) { + vector::append(&mut self.bytes, r.bytes) + } + + /// Appends bytes which must be in valid utf8 format. + public fun append_utf8(self: &mut String, bytes: vector) { + append(self, utf8(bytes)) + } + + /// Insert the other string at the byte index in given string. The index must be at a valid utf8 char + /// boundary. + public fun insert(self: &mut String, at: u64, o: String) { + let bytes = &self.bytes; + assert!(at <= vector::length(bytes) && internal_is_char_boundary(bytes, at), EINVALID_INDEX); + let l = length(self); + let front = sub_string(self, 0, at); + let end = sub_string(self, at, l); + append(&mut front, o); + append(&mut front, end); + *self = front; + } + + /// Returns a sub-string using the given byte indices, where `i` is the first byte position and `j` is the start + /// of the first byte not included (or the length of the string). The indices must be at valid utf8 char boundaries, + /// guaranteeing that the result is valid utf8. + public fun sub_string(self: &String, i: u64, j: u64): String { + let bytes = &self.bytes; + let l = vector::length(bytes); + assert!( + j <= l && i <= j && internal_is_char_boundary(bytes, i) && internal_is_char_boundary(bytes, j), + EINVALID_INDEX + ); + String { bytes: internal_sub_string(bytes, i, j) } + } + + /// Computes the index of the first occurrence of a string. Returns `length(s)` if no occurrence found. + public fun index_of(self: &String, r: &String): u64 { + internal_index_of(&self.bytes, &r.bytes) + } + + // Native API + public native fun internal_check_utf8(v: &vector): bool; + native fun internal_is_char_boundary(v: &vector, i: u64): bool; + native fun internal_sub_string(v: &vector, i: u64, j: u64): vector; + native fun internal_index_of(v: &vector, r: &vector): u64; +} diff --git a/third_party/move/tools/move-decompiler/tests/testsuite.rs b/third_party/move/tools/move-decompiler/tests/testsuite.rs new file mode 100644 index 00000000000000..c7a118bcb7d4fa --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/testsuite.rs @@ -0,0 +1,86 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use codespan_reporting::{diagnostic::Severity, term::termcolor::Buffer}; +use move_compiler_v2::{logging, run_move_compiler_for_analysis}; +use move_decompiler::{Decompiler, Options}; +use move_model::metadata::LanguageVersion; +use move_prover_test_utils::{baseline_test, extract_test_directives}; +use std::path::Path; + +/// Extension for expected output files +pub const EXP_EXT: &str = "exp"; + +datatest_stable::harness!(test_runner, "tests", r".*\.move$"); + +fn test_runner(path: &Path) -> datatest_stable::Result<()> { + logging::setup_logging_for_testing(); + let path_str = path.display().to_string(); + let mut compiler_options = move_compiler_v2::Options { + sources_deps: extract_test_directives(path, "// dep:")?, + sources: vec![path_str.clone()], + dependencies: vec!["./tests".to_string()], + /*if extract_test_directives(path, "// no-stdlib")?.is_empty() { + vec![path_from_crate_root("../../move-stdlib/sources")] + } else { + vec![] + }, + */ + named_address_mapping: vec![ + "std=0x1".to_string(), + "aptos_std=0x1".to_string(), + "aptos_framework=0x1".to_string(), + ], + ..Default::default() + }; + compiler_options = compiler_options.set_language_version(LanguageVersion::latest()); + let mut output = String::new(); + let mut error_writer = Buffer::no_color(); + match run_move_compiler_for_analysis(&mut error_writer, compiler_options) { + Err(e) => { + output.push_str(&format!( + "--- Aborting with compilation errors:\n{:#}\n{}\n", + e, + String::from_utf8_lossy(&error_writer.into_inner()) + )); + }, + Ok(env) => { + let decompiler_options = Options { + no_expressions: false, + ..Options::default() + }; + let mut decompiler = Decompiler::new(decompiler_options); + for module_env in env.get_modules() { + if !module_env.is_primary_target() { + continue; + } + if let Some(compiled_module) = module_env.get_verified_module() { + let source_map = module_env.get_source_map().cloned().unwrap_or_else(|| { + let mut bytes = vec![]; + compiled_module + .serialize(&mut bytes) + .expect("expected serialization success"); + decompiler.empty_source_map(&module_env.get_full_name_str(), &bytes) + }); + output += "\n"; + output += &decompiler.decompile_module(compiled_module.clone(), source_map); + } + } + if decompiler + .env() + .check_diag(&mut error_writer, Severity::Warning, "decompilation") + .is_err() + { + output.push_str(&format!( + "--- Decompilation errors:\n{}\n", + String::from_utf8_lossy(&error_writer.into_inner()) + )); + } + }, + } + // Generate/check baseline. + let baseline_path = path.with_extension(EXP_EXT); + baseline_test::verify_or_update_baseline(baseline_path.as_path(), &output)?; + Ok(()) +} diff --git a/third_party/move/tools/move-decompiler/tests/vector.exp b/third_party/move/tools/move-decompiler/tests/vector.exp new file mode 100644 index 00000000000000..669ac6c7cf606d --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/vector.exp @@ -0,0 +1,176 @@ + +module 0x1::vector { + public native fun empty(): vector; + public native fun length(self: &vector): u64; + public native fun borrow(self: &vector, i: u64): ∈ + public native fun borrow_mut(self: &mut vector, i: u64): &mut Element; + public native fun push_back(self: &mut vector, e: Element); + public native fun pop_back(self: &mut vector): Element; + public native fun destroy_empty(self: vector); + public native fun swap(self: &mut vector, i: u64, j: u64); + public fun contains(self: &vector, e: &Element): bool { + let _t2 = 0; + 'l0: loop { + loop { + if (!(_t2 < length(self))) break; + if (!(borrow(self, _t2) == e)) { + let _t2 = _t2 + 1; + continue 'l0 + }; + return true + }; + break + }; + false + } + public fun index_of(self: &vector, e: &Element): (bool, u64) { + let _t2 = 0; + 'l0: loop { + loop { + if (!(_t2 < length(self))) break; + if (!(borrow(self, _t2) == e)) { + _t2 = _t2 + 1; + continue 'l0 + }; + return (true, _t2) + }; + break + }; + (false, 0) + } + public fun range(start: u64, end: u64): vector { + range_with_step(start, end, 1) + } + public fun range_with_step(start: u64, end: u64, step: u64): vector { + let _t4; + if (step > 0) _t4 = empty() else { + abort 131075; + while (start < end) { + push_back(&mut _t4, start); + let start = start + step; + continue + } + }; + _t4 + } + public fun append(self: &mut vector, other: vector) { + reverse(&mut other); + reverse_append(self, other); + } + public fun reverse(self: &mut vector) { + reverse_slice(self, 0, length(/*freeze*/self)); + } + public fun reverse_append(self: &mut vector, other: vector) { + let _t2 = length(&other); + while (_t2 > 0) { + push_back(self, pop_back(&mut other)); + let _t2 = _t2 - 1; + continue + }; + destroy_empty(other); + } + public fun insert(self: &mut vector, i: u64, e: Element) { + if (i <= length(/*freeze*/self)) push_back(self, e) else { + abort 131072; + while (i < length(/*freeze*/self)) { + swap(self, i, length(/*freeze*/self)); + let i = i + 1; + continue + } + }; + } + public fun is_empty(self: &vector): bool { + length(self) == 0 + } + public fun remove(self: &mut vector, i: u64): Element { + let _t2 = length(/*freeze*/self); + if (i >= _t2) { + abort 131072; + while (i < _t2) { + let i = i + 1; + swap(self, i, i); + continue + } + }; + pop_back(self) + } + public fun remove_value(self: &mut vector, val: &Element): vector { + let _t3; + let (_t7,_t8) = index_of(/*freeze*/self, val); + if (_t7) { + let _t12 = empty(); + push_back(&mut _t12, remove(self, _t8)); + _t3 = _t12 + } + else _t3 = empty(); + _t3 + } + public fun reverse_slice(self: &mut vector, left: u64, right: u64) { + loop { + if (left <= right) break; + abort 131073 + }; + if (left == right) { + return (); + while (left < right) { + swap(self, left, right); + let left = left + 1; + let right = right - 1; + continue + } + }; + } + public fun rotate(self: &mut vector, rot: u64): u64 { + rotate_slice(self, 0, rot, length(/*freeze*/self)) + } + public fun rotate_slice(self: &mut vector, left: u64, rot: u64, right: u64): u64 { + reverse_slice(self, left, rot); + reverse_slice(self, rot, right); + reverse_slice(self, left, right); + left + (right - rot) + } + public fun singleton(e: Element): vector { + let _t1 = empty(); + push_back(&mut _t1, e); + _t1 + } + public fun slice(self: &vector, start: u64, end: u64): vector { + let _t5; + if (start <= end) _t5 = end <= length(self) + else _t5 = false; + if (_t5) _t6 = empty() else { + abort 131076; + while (start < end) { + push_back(&mut _t6, *borrow(self, start)); + let start = start + 1; + continue + } + }; + _t6 + } + public fun swap_remove(self: &mut vector, i: u64): Element { + loop { + if (!is_empty(/*freeze*/self)) break; + abort 131072 + }; + swap(self, i, length(/*freeze*/self) - 1); + pop_back(self) + } + public fun trim(self: &mut vector, new_len: u64): vector { + let _t2 = trim_reverse(self, new_len); + reverse(&mut _t2); + _t2 + } + public fun trim_reverse(self: &mut vector, new_len: u64): vector { + let _t3; + if (new_len <= length(/*freeze*/self)) _t3 = empty() else { + abort 131072; + while (new_len < length(/*freeze*/self)) { + push_back(&mut _t3, pop_back(self)); + let _t2 = length(/*freeze*/self) - 1; + continue + } + }; + _t3 + } +} diff --git a/third_party/move/tools/move-decompiler/tests/vector.move b/third_party/move/tools/move-decompiler/tests/vector.move new file mode 100644 index 00000000000000..2f67cc6caf998e --- /dev/null +++ b/third_party/move/tools/move-decompiler/tests/vector.move @@ -0,0 +1,671 @@ +// no-stdlib + +/// A variable-sized container that can hold any type. Indexing is 0-based, and +/// vectors are growable. This module has many native functions. +/// Verification of modules that use this one uses model functions that are implemented +/// directly in Boogie. The specification language has built-in functions operations such +/// as `singleton_vector`. There are some helper functions defined here for specifications in other +/// modules as well. +/// +/// >Note: We did not verify most of the +/// Move functions here because many have loops, requiring loop invariants to prove, and +/// the return on investment didn't seem worth it for these simple functions. +module std::vector { + /// The index into the vector is out of bounds + const EINDEX_OUT_OF_BOUNDS: u64 = 0x20000; + + /// The index into the vector is out of bounds + const EINVALID_RANGE: u64 = 0x20001; + + /// The length of the vectors are not equal. + const EVECTORS_LENGTH_MISMATCH: u64 = 0x20002; + + /// The step provided in `range` is invalid, must be greater than zero. + const EINVALID_STEP: u64 = 0x20003; + + /// The range in `slice` is invalid. + const EINVALID_SLICE_RANGE: u64 = 0x20004; + + #[bytecode_instruction] + /// Create an empty vector. + native public fun empty(): vector; + + #[bytecode_instruction] + /// Return the length of the vector. + native public fun length(self: &vector): u64; + + #[bytecode_instruction] + /// Acquire an immutable reference to the `i`th element of the vector `self`. + /// Aborts if `i` is out of bounds. + native public fun borrow(self: &vector, i: u64): ∈ + + #[bytecode_instruction] + /// Add element `e` to the end of the vector `self`. + native public fun push_back(self: &mut vector, e: Element); + + #[bytecode_instruction] + /// Return a mutable reference to the `i`th element in the vector `self`. + /// Aborts if `i` is out of bounds. + native public fun borrow_mut(self: &mut vector, i: u64): &mut Element; + + #[bytecode_instruction] + /// Pop an element from the end of vector `self`. + /// Aborts if `self` is empty. + native public fun pop_back(self: &mut vector): Element; + + #[bytecode_instruction] + /// Destroy the vector `self`. + /// Aborts if `self` is not empty. + native public fun destroy_empty(self: vector); + + #[bytecode_instruction] + /// Swaps the elements at the `i`th and `j`th indices in the vector `self`. + /// Aborts if `i` or `j` is out of bounds. + native public fun swap(self: &mut vector, i: u64, j: u64); + + /// Return an vector of size one containing element `e`. + public fun singleton(e: Element): vector { + let v = empty(); + push_back(&mut v, e); + v + } + spec singleton { + aborts_if false; + ensures result == vec(e); + } + + /// Reverses the order of the elements in the vector `self` in place. + public fun reverse(self: &mut vector) { + let len = length(self); + reverse_slice(self, 0, len); + } + + spec reverse { + pragma intrinsic = true; + } + + /// Reverses the order of the elements [left, right) in the vector `self` in place. + public fun reverse_slice(self: &mut vector, left: u64, right: u64) { + assert!(left <= right, EINVALID_RANGE); + if (left == right) return; + right = right - 1; + while (left < right) { + swap(self, left, right); + left = left + 1; + right = right - 1; + } + } + spec reverse_slice { + pragma intrinsic = true; + } + + /// Pushes all of the elements of the `other` vector into the `self` vector. + public fun append(self: &mut vector, other: vector) { + reverse(&mut other); + reverse_append(self, other); + } + spec append { + pragma intrinsic = true; + } + spec is_empty { + pragma intrinsic = true; + } + + /// Pushes all of the elements of the `other` vector into the `self` vector. + public fun reverse_append(self: &mut vector, other: vector) { + let len = length(&other); + while (len > 0) { + push_back(self, pop_back(&mut other)); + len = len - 1; + }; + destroy_empty(other); + } + spec reverse_append { + pragma intrinsic = true; + } + + /// Trim a vector to a smaller size, returning the evicted elements in order + public fun trim(self: &mut vector, new_len: u64): vector { + let res = trim_reverse(self, new_len); + reverse(&mut res); + res + } + spec trim { + pragma intrinsic = true; + } + + /// Trim a vector to a smaller size, returning the evicted elements in reverse order + public fun trim_reverse(self: &mut vector, new_len: u64): vector { + let len = length(self); + assert!(new_len <= len, EINDEX_OUT_OF_BOUNDS); + let result = empty(); + while (new_len < len) { + push_back(&mut result, pop_back(self)); + len = len - 1; + }; + result + } + spec trim_reverse { + pragma intrinsic = true; + } + + + /// Return `true` if the vector `self` has no elements and `false` otherwise. + public fun is_empty(self: &vector): bool { + length(self) == 0 + } + + /// Return true if `e` is in the vector `self`. + public fun contains(self: &vector, e: &Element): bool { + let i = 0; + let len = length(self); + while (i < len) { + if (borrow(self, i) == e) return true; + i = i + 1; + }; + false + } + spec contains { + pragma intrinsic = true; + } + + /// Return `(true, i)` if `e` is in the vector `self` at index `i`. + /// Otherwise, returns `(false, 0)`. + public fun index_of(self: &vector, e: &Element): (bool, u64) { + let i = 0; + let len = length(self); + while (i < len) { + if (borrow(self, i) == e) return (true, i); + i = i + 1; + }; + (false, 0) + } + spec index_of { + pragma intrinsic = true; + } + + /// Return `(true, i)` if there's an element that matches the predicate. If there are multiple elements that match + /// the predicate, only the index of the first one is returned. + /// Otherwise, returns `(false, 0)`. + public inline fun find(self: &vector, f: |&Element|bool): (bool, u64) { + let find = false; + let found_index = 0; + let i = 0; + let len = length(self); + while (i < len) { + // Cannot call return in an inline function so we need to resort to break here. + if (f(borrow(self, i))) { + find = true; + found_index = i; + break + }; + i = i + 1; + }; + (find, found_index) + } + + /// Insert a new element at position 0 <= i <= length, using O(length - i) time. + /// Aborts if out of bounds. + public fun insert(self: &mut vector, i: u64, e: Element) { + let len = length(self); + assert!(i <= len, EINDEX_OUT_OF_BOUNDS); + push_back(self, e); + while (i < len) { + swap(self, i, len); + i = i + 1; + }; + } + spec insert { + pragma intrinsic = true; + } + + /// Remove the `i`th element of the vector `self`, shifting all subsequent elements. + /// This is O(n) and preserves ordering of elements in the vector. + /// Aborts if `i` is out of bounds. + public fun remove(self: &mut vector, i: u64): Element { + let len = length(self); + // i out of bounds; abort + if (i >= len) abort EINDEX_OUT_OF_BOUNDS; + + len = len - 1; + while (i < len) swap(self, i, { i = i + 1; i }); + pop_back(self) + } + spec remove { + pragma intrinsic = true; + } + + /// Remove the first occurrence of a given value in the vector `self` and return it in a vector, shifting all + /// subsequent elements. + /// This is O(n) and preserves ordering of elements in the vector. + /// This returns an empty vector if the value isn't present in the vector. + /// Note that this cannot return an option as option uses vector and there'd be a circular dependency between option + /// and vector. + public fun remove_value(self: &mut vector, val: &Element): vector { + // This doesn't cost a O(2N) run time as index_of scans from left to right and stops when the element is found, + // while remove would continue from the identified index to the end of the vector. + let (found, index) = index_of(self, val); + if (found) { + vector[remove(self, index)] + } else { + vector[] + } + } + spec remove_value { + pragma intrinsic = true; + } + + /// Swap the `i`th element of the vector `self` with the last element and then pop the vector. + /// This is O(1), but does not preserve ordering of elements in the vector. + /// Aborts if `i` is out of bounds. + public fun swap_remove(self: &mut vector, i: u64): Element { + assert!(!is_empty(self), EINDEX_OUT_OF_BOUNDS); + let last_idx = length(self) - 1; + swap(self, i, last_idx); + pop_back(self) + } + spec swap_remove { + pragma intrinsic = true; + } + + /// Apply the function to each element in the vector, consuming it. + public inline fun for_each(self: vector, f: |Element|) { + reverse(&mut self); // We need to reverse the vector to consume it efficiently + for_each_reverse(self, |e| f(e)); + } + + /// Apply the function to each element in the vector, consuming it. + public inline fun for_each_reverse(self: vector, f: |Element|) { + let len = length(&self); + while (len > 0) { + f(pop_back(&mut self)); + len = len - 1; + }; + destroy_empty(self) + } + + /// Apply the function to a reference of each element in the vector. + public inline fun for_each_ref(self: &vector, f: |&Element|) { + let i = 0; + let len = length(self); + while (i < len) { + f(borrow(self, i)); + i = i + 1 + } + } + + /// Apply the function to each pair of elements in the two given vectors, consuming them. + public inline fun zip(self: vector, v2: vector, f: |Element1, Element2|) { + // We need to reverse the vectors to consume it efficiently + reverse(&mut self); + reverse(&mut v2); + zip_reverse(self, v2, |e1, e2| f(e1, e2)); + } + + /// Apply the function to each pair of elements in the two given vectors in the reverse order, consuming them. + /// This errors out if the vectors are not of the same length. + public inline fun zip_reverse( + self: vector, + v2: vector, + f: |Element1, Element2|, + ) { + let len = length(&self); + // We can't use the constant EVECTORS_LENGTH_MISMATCH here as all calling code would then need to define it + // due to how inline functions work. + assert!(len == length(&v2), 0x20002); + while (len > 0) { + f(pop_back(&mut self), pop_back(&mut v2)); + len = len - 1; + }; + destroy_empty(self); + destroy_empty(v2); + } + + /// Apply the function to the references of each pair of elements in the two given vectors. + /// This errors out if the vectors are not of the same length. + public inline fun zip_ref( + self: &vector, + v2: &vector, + f: |&Element1, &Element2|, + ) { + let len = length(self); + // We can't use the constant EVECTORS_LENGTH_MISMATCH here as all calling code would then need to define it + // due to how inline functions work. + assert!(len == length(v2), 0x20002); + let i = 0; + while (i < len) { + f(borrow(self, i), borrow(v2, i)); + i = i + 1 + } + } + + /// Apply the function to a reference of each element in the vector with its index. + public inline fun enumerate_ref(self: &vector, f: |u64, &Element|) { + let i = 0; + let len = length(self); + while (i < len) { + f(i, borrow(self, i)); + i = i + 1; + }; + } + + /// Apply the function to a mutable reference to each element in the vector. + public inline fun for_each_mut(self: &mut vector, f: |&mut Element|) { + let i = 0; + let len = length(self); + while (i < len) { + f(borrow_mut(self, i)); + i = i + 1 + } + } + + /// Apply the function to mutable references to each pair of elements in the two given vectors. + /// This errors out if the vectors are not of the same length. + public inline fun zip_mut( + self: &mut vector, + v2: &mut vector, + f: |&mut Element1, &mut Element2|, + ) { + let i = 0; + let len = length(self); + // We can't use the constant EVECTORS_LENGTH_MISMATCH here as all calling code would then need to define it + // due to how inline functions work. + assert!(len == length(v2), 0x20002); + while (i < len) { + f(borrow_mut(self, i), borrow_mut(v2, i)); + i = i + 1 + } + } + + /// Apply the function to a mutable reference of each element in the vector with its index. + public inline fun enumerate_mut(self: &mut vector, f: |u64, &mut Element|) { + let i = 0; + let len = length(self); + while (i < len) { + f(i, borrow_mut(self, i)); + i = i + 1; + }; + } + + /// Fold the function over the elements. For example, `fold(vector[1,2,3], 0, f)` will execute + /// `f(f(f(0, 1), 2), 3)` + public inline fun fold( + self: vector, + init: Accumulator, + f: |Accumulator,Element|Accumulator + ): Accumulator { + let accu = init; + for_each(self, |elem| accu = f(accu, elem)); + accu + } + + /// Fold right like fold above but working right to left. For example, `fold(vector[1,2,3], 0, f)` will execute + /// `f(1, f(2, f(3, 0)))` + public inline fun foldr( + self: vector, + init: Accumulator, + f: |Element, Accumulator|Accumulator + ): Accumulator { + let accu = init; + for_each_reverse(self, |elem| accu = f(elem, accu)); + accu + } + + /// Map the function over the references of the elements of the vector, producing a new vector without modifying the + /// original vector. + public inline fun map_ref( + self: &vector, + f: |&Element|NewElement + ): vector { + let result = vector[]; + for_each_ref(self, |elem| push_back(&mut result, f(elem))); + result + } + + /// Map the function over the references of the element pairs of two vectors, producing a new vector from the return + /// values without modifying the original vectors. + public inline fun zip_map_ref( + self: &vector, + v2: &vector, + f: |&Element1, &Element2|NewElement + ): vector { + // We can't use the constant EVECTORS_LENGTH_MISMATCH here as all calling code would then need to define it + // due to how inline functions work. + assert!(length(self) == length(v2), 0x20002); + + let result = vector[]; + zip_ref(self, v2, |e1, e2| push_back(&mut result, f(e1, e2))); + result + } + + /// Map the function over the elements of the vector, producing a new vector. + public inline fun map( + self: vector, + f: |Element|NewElement + ): vector { + let result = vector[]; + for_each(self, |elem| push_back(&mut result, f(elem))); + result + } + + /// Map the function over the element pairs of the two vectors, producing a new vector. + public inline fun zip_map( + self: vector, + v2: vector, + f: |Element1, Element2|NewElement + ): vector { + // We can't use the constant EVECTORS_LENGTH_MISMATCH here as all calling code would then need to define it + // due to how inline functions work. + assert!(length(&self) == length(&v2), 0x20002); + + let result = vector[]; + zip(self, v2, |e1, e2| push_back(&mut result, f(e1, e2))); + result + } + + /// Filter the vector using the boolean function, removing all elements for which `p(e)` is not true. + public inline fun filter( + self: vector, + p: |&Element|bool + ): vector { + let result = vector[]; + for_each(self, |elem| { + if (p(&elem)) push_back(&mut result, elem); + }); + result + } + + /// Partition, sorts all elements for which pred is true to the front. + /// Preserves the relative order of the elements for which pred is true, + /// BUT NOT for the elements for which pred is false. + public inline fun partition( + self: &mut vector, + pred: |&Element|bool + ): u64 { + let i = 0; + let len = length(self); + while (i < len) { + if (!pred(borrow(self, i))) break; + i = i + 1; + }; + let p = i; + i = i + 1; + while (i < len) { + if (pred(borrow(self, i))) { + swap(self, p, i); + p = p + 1; + }; + i = i + 1; + }; + p + } + + /// rotate(&mut [1, 2, 3, 4, 5], 2) -> [3, 4, 5, 1, 2] in place, returns the split point + /// ie. 3 in the example above + public fun rotate( + self: &mut vector, + rot: u64 + ): u64 { + let len = length(self); + rotate_slice(self, 0, rot, len) + } + spec rotate { + pragma intrinsic = true; + } + + /// Same as above but on a sub-slice of an array [left, right) with left <= rot <= right + /// returns the + public fun rotate_slice( + self: &mut vector, + left: u64, + rot: u64, + right: u64 + ): u64 { + reverse_slice(self, left, rot); + reverse_slice(self, rot, right); + reverse_slice(self, left, right); + left + (right - rot) + } + spec rotate_slice { + pragma intrinsic = true; + } + + /// Partition the array based on a predicate p, this routine is stable and thus + /// preserves the relative order of the elements in the two partitions. + public inline fun stable_partition( + self: &mut vector, + p: |&Element|bool + ): u64 { + let len = length(self); + let t = empty(); + let f = empty(); + while (len > 0) { + let e = pop_back(self); + if (p(&e)) { + push_back(&mut t, e); + } else { + push_back(&mut f, e); + }; + len = len - 1; + }; + let pos = length(&t); + reverse_append(self, t); + reverse_append(self, f); + pos + } + + /// Return true if any element in the vector satisfies the predicate. + public inline fun any( + self: &vector, + p: |&Element|bool + ): bool { + let result = false; + let i = 0; + while (i < length(self)) { + result = p(borrow(self, i)); + if (result) { + break + }; + i = i + 1 + }; + result + } + + /// Return true if all elements in the vector satisfy the predicate. + public inline fun all( + self: &vector, + p: |&Element|bool + ): bool { + let result = true; + let i = 0; + while (i < length(self)) { + result = p(borrow(self, i)); + if (!result) { + break + }; + i = i + 1 + }; + result + } + + /// Destroy a vector, just a wrapper around for_each_reverse with a descriptive name + /// when used in the context of destroying a vector. + public inline fun destroy( + self: vector, + d: |Element| + ) { + for_each_reverse(self, |e| d(e)) + } + + public fun range(start: u64, end: u64): vector { + range_with_step(start, end, 1) + } + + public fun range_with_step(start: u64, end: u64, step: u64): vector { + assert!(step > 0, EINVALID_STEP); + + let vec = vector[]; + while (start < end) { + push_back(&mut vec, start); + start = start + step; + }; + vec + } + + public fun slice( + self: &vector, + start: u64, + end: u64 + ): vector { + assert!(start <= end && end <= length(self), EINVALID_SLICE_RANGE); + + let vec = vector[]; + while (start < end) { + push_back(&mut vec, *borrow(self, start)); + start = start + 1; + }; + vec + } + + // ================================================================= + // Module Specification + + spec module {} // Switch to module documentation context + + /// # Helper Functions + + spec module { + /// Check if `self` is equal to the result of adding `e` at the end of `v2` + fun eq_push_back(self: vector, v2: vector, e: Element): bool { + len(self) == len(v2) + 1 && + self[len(self)-1] == e && + self[0..len(self)-1] == v2[0..len(v2)] + } + + /// Check if `self` is equal to the result of concatenating `v1` and `v2` + fun eq_append(self: vector, v1: vector, v2: vector): bool { + len(self) == len(v1) + len(v2) && + self[0..len(v1)] == v1 && + self[len(v1)..len(self)] == v2 + } + + /// Check `self` is equal to the result of removing the first element of `v2` + fun eq_pop_front(self: vector, v2: vector): bool { + len(self) + 1 == len(v2) && + self == v2[1..len(v2)] + } + + /// Check that `v1` is equal to the result of removing the element at index `i` from `v2`. + fun eq_remove_elem_at_index(i: u64, v1: vector, v2: vector): bool { + len(v1) + 1 == len(v2) && + v1[0..i] == v2[0..i] && + v1[i..len(v1)] == v2[i + 1..len(v2)] + } + + /// Check if `self` contains `e`. + fun spec_contains(self: vector, e: Element): bool { + exists x in self: x == e + } + } + +}