From 80946f3ea139d7b351b45ce90b710102d4c5d47a Mon Sep 17 00:00:00 2001 From: Andrii Date: Tue, 24 Feb 2026 17:11:03 +0200 Subject: [PATCH 01/21] Dynamic field UID fix --- Cargo.lock | 605 +++++++++++------- .../src/boogie_backend/bytecode_translator.rs | 21 +- .../src/boogie_backend/lib.rs | 74 ++- .../src/boogie_backend/prelude/native.bpl | 17 + .../src/dynamic_field_analysis.rs | 510 ++++++++++++++- .../inputs/dynamic_field/uid_param.ok.move | 24 + .../dynamic_field/uid_param_exists.ok.move | 20 + .../dynamic_field/uid_param_remove.ok.move | 20 + .../dynamic_field/uid_param_target.ok.move | 42 ++ .../dynamic_field/uid_param.ok.move.snap | 6 + .../uid_param_exists.ok.move.snap | 6 + .../uid_param_remove.ok.move.snap | 6 + .../uid_param_target.ok.move.snap | 6 + 13 files changed, 1069 insertions(+), 288 deletions(-) create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param.ok.move create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_exists.ok.move create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_target.ok.move create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap diff --git a/Cargo.lock b/Cargo.lock index 10a2a76f57..16fb2ad11b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "ar_archive_writer" @@ -139,7 +139,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -150,7 +150,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -167,9 +167,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.4" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" dependencies = [ "aws-lc-sys", "zeroize", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -258,9 +258,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bitmaps" @@ -332,9 +332,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byte-slice-cast" @@ -350,9 +350,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "camino" @@ -362,9 +362,9 @@ checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" [[package]] name = "cc" -version = "1.2.54" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -386,9 +386,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -422,9 +422,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.56" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.56" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", @@ -452,14 +452,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "cmake" @@ -673,7 +673,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -684,7 +684,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -714,9 +714,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -765,7 +765,7 @@ dependencies = [ "glob", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -797,7 +797,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -842,7 +842,7 @@ dependencies = [ [[package]] name = "enum-compat-util" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "serde_yaml", ] @@ -914,9 +914,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed-hash" @@ -938,9 +938,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1002,9 +1002,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1017,9 +1017,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1027,15 +1027,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1044,38 +1044,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1085,7 +1085,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1133,6 +1132,19 @@ dependencies = [ "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "glob" version = "0.3.3" @@ -1158,7 +1170,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "ignore", "walkdir", ] @@ -1382,13 +1394,12 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "http 1.4.0", "http-body 1.0.1", @@ -1506,6 +1517,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -1589,7 +1606,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1623,9 +1640,9 @@ checksum = "dc1804bdb6a9784758b200007273a8b84e2b0b0b97a8f1e18e763eceb3e9f98a" [[package]] name = "insta" -version = "1.46.1" +version = "1.46.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" +checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" dependencies = [ "console", "once_cell", @@ -1710,9 +1727,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "f4eacb0641a310445a4c513f2a5e23e19952e269c6a38887254d5f837a305506" dependencies = [ "once_cell", "wasm-bindgen", @@ -1720,9 +1737,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ "cpufeatures", ] @@ -1812,11 +1829,17 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libm" @@ -1830,9 +1853,9 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", - "redox_syscall 0.7.0", + "redox_syscall 0.7.1", ] [[package]] @@ -1855,9 +1878,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -1907,9 +1930,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -1953,17 +1976,17 @@ dependencies = [ [[package]] name = "move-abstract-interpreter" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" [[package]] name = "move-abstract-stack" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" [[package]] name = "move-binary-format" version = "0.0.3" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "enum-compat-util", @@ -1979,12 +2002,12 @@ dependencies = [ [[package]] name = "move-borrow-graph" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" [[package]] name = "move-bytecode-source-map" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "bcs", @@ -2000,7 +2023,7 @@ dependencies = [ [[package]] name = "move-bytecode-utils" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "indexmap 2.13.0", @@ -2013,7 +2036,7 @@ dependencies = [ [[package]] name = "move-bytecode-verifier" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "move-abstract-interpreter", "move-abstract-stack", @@ -2029,7 +2052,7 @@ dependencies = [ [[package]] name = "move-bytecode-verifier-meter" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "move-binary-format", "move-core-types", @@ -2039,7 +2062,7 @@ dependencies = [ [[package]] name = "move-command-line-common" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "bcs", @@ -2060,7 +2083,7 @@ dependencies = [ [[package]] name = "move-compiler" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "bcs", @@ -2096,7 +2119,7 @@ dependencies = [ [[package]] name = "move-core-types" version = "0.0.4" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "bcs", @@ -2121,7 +2144,7 @@ dependencies = [ [[package]] name = "move-coverage" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "bcs", @@ -2146,7 +2169,7 @@ dependencies = [ [[package]] name = "move-disassembler" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "bcs", @@ -2167,7 +2190,7 @@ dependencies = [ [[package]] name = "move-docgen" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "clap", @@ -2190,7 +2213,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "codespan-reporting", @@ -2208,7 +2231,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode-syntax" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "hex", @@ -2221,7 +2244,7 @@ dependencies = [ [[package]] name = "move-ir-types" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "hex", "move-command-line-common", @@ -2259,7 +2282,7 @@ dependencies = [ [[package]] name = "move-model" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "codespan", @@ -2284,7 +2307,7 @@ dependencies = [ [[package]] name = "move-model-2" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "bcs", @@ -2308,7 +2331,7 @@ dependencies = [ [[package]] name = "move-package" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "clap", @@ -2345,11 +2368,11 @@ dependencies = [ [[package]] name = "move-proc-macros" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "enum-compat-util", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2391,7 +2414,7 @@ dependencies = [ [[package]] name = "move-regex-borrow-graph" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "itertools 0.10.5", "move-binary-format", @@ -2434,7 +2457,7 @@ dependencies = [ [[package]] name = "move-stdlib" version = "0.1.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "anyhow", "hex", @@ -2453,7 +2476,7 @@ dependencies = [ [[package]] name = "move-stdlib-natives" version = "0.1.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "hex", "move-binary-format", @@ -2468,7 +2491,7 @@ dependencies = [ [[package]] name = "move-symbol-pool" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "once_cell", "phf", @@ -2478,7 +2501,7 @@ dependencies = [ [[package]] name = "move-trace-format" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "move-binary-format", "move-core-types", @@ -2490,7 +2513,7 @@ dependencies = [ [[package]] name = "move-vm-config" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "move-binary-format", "once_cell", @@ -2499,7 +2522,7 @@ dependencies = [ [[package]] name = "move-vm-profiler" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "move-trace-format", "move-vm-config", @@ -2512,7 +2535,7 @@ dependencies = [ [[package]] name = "move-vm-runtime" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "better_any", "fail", @@ -2531,7 +2554,7 @@ dependencies = [ [[package]] name = "move-vm-types" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" +source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" dependencies = [ "bcs", "move-binary-format", @@ -2557,17 +2580,17 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", "openssl", - "openssl-probe 0.1.6", + "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.1", + "security-framework", "security-framework-sys", "tempfile", ] @@ -2578,7 +2601,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -2590,7 +2613,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -2741,7 +2764,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", @@ -2758,15 +2781,9 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -2889,7 +2906,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3011,9 +3028,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -3021,9 +3038,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -3031,22 +3048,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", "sha2 0.10.9", @@ -3104,7 +3121,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3133,7 +3150,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3198,6 +3215,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "primitive-types" version = "0.10.1" @@ -3270,13 +3297,13 @@ dependencies = [ [[package]] name = "proptest" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.10.0", + "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -3295,9 +3322,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psm" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa96cb91275ed31d6da3e983447320c4eb219ac180fa1679a0889ff32861e2d" +checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" dependencies = [ "ar_archive_writer", "cc", @@ -3514,16 +3541,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -3554,14 +3581,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -3571,9 +3598,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -3582,9 +3609,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" @@ -3648,11 +3675,11 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -3681,10 +3708,10 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe 0.2.1", + "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework", ] [[package]] @@ -3737,9 +3764,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -3773,9 +3800,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -3791,39 +3818,32 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", + "bitflags 2.11.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] -name = "security-framework" -version = "3.5.1" +name = "security-framework-sys" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", "core-foundation-sys", "libc", - "security-framework-sys", ] [[package]] -name = "security-framework-sys" -version = "2.15.0" +name = "semver" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" @@ -3876,7 +3896,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3911,7 +3931,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3947,7 +3967,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.2.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -3963,7 +3983,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4107,9 +4127,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "slug" @@ -4155,9 +4175,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" +checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" dependencies = [ "cc", "cfg-if", @@ -4230,9 +4250,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -4259,7 +4279,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4309,12 +4329,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.24.0" +version = "3.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4387,7 +4407,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4398,7 +4418,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4434,9 +4454,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -4455,9 +4475,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.26" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -4498,7 +4518,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4667,7 +4687,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4804,9 +4824,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" @@ -4820,6 +4840,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" @@ -4883,7 +4909,7 @@ checksum = "a1935e10c6f04d22688d07c0790f2fc0e1b1c5c2c55bc0cc87ed67656e587dd8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4953,6 +4979,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasite" version = "0.1.0" @@ -4961,9 +4996,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "05d7d0fce354c88b7982aec4400b3e7fcf723c32737cef571bd165f7613557ee" dependencies = [ "cfg-if", "once_cell", @@ -4974,9 +5009,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "ee85afca410ac4abba5b584b12e77ea225db6ee5471d0aebaae0861166f9378a" dependencies = [ "cfg-if", "futures-util", @@ -4988,9 +5023,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "55839b71ba921e4f75b674cb16f843f4b1f3b26ddfcb3454de1cf65cc021ec0f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4998,31 +5033,65 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "caf2e969c2d60ff52e7e98b7392ff1588bffdd1ccd4769eba27222fd3d621571" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "0861f0dcdf46ea819407495634953cdcc8a8c7215ab799a7a7ce366be71c7b30" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", +] + [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "10053fbf9a374174094915bbce141e87a6bf32ecd9a002980db4b638405e8962" dependencies = [ "js-sys", "wasm-bindgen", @@ -5034,14 +5103,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -5115,7 +5184,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5126,7 +5195,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5417,6 +5486,88 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" @@ -5467,28 +5618,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.36" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dafd85c832c1b68bbb4ec0c72c7f6f4fc5179627d2bc7c26b30e4c0cc11e76cc" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.36" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cb7e4e8436d9db52fbd6625dbf2f45243ab84994a72882ec8227b99e72b439a" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5508,7 +5659,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -5548,14 +5699,14 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zmij" -version = "1.0.17" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zstd" diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs index 18b1b0299e..c4fbe9e000 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs @@ -414,15 +414,26 @@ impl<'env> BoogieTranslator<'env> { if !translated_types.insert(struct_name) { continue; } + // Check if this struct type has dynamic fields - if so, it should not be opaque + let struct_type = Type::Datatype( + struct_env.module_env.get_id(), + struct_env.get_id(), + type_inst.to_vec(), + ); + let has_dynamic_fields = dynamic_field_analysis::get_env_info(self.env) + .dynamic_field_names_values(&struct_type) + .next() + .is_some(); StructTranslator { parent: self, struct_env, type_inst: type_inst.as_slice(), - is_opaque: !mono_info.is_used_datatype( - self.env, - self.targets, - &struct_env.get_qualified_id(), - ), + is_opaque: !has_dynamic_fields + && !mono_info.is_used_datatype( + self.env, + self.targets, + &struct_env.get_qualified_id(), + ), } .translate(); } diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs index 276c5fec42..3cff38fc30 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs @@ -103,6 +103,7 @@ struct DynamicFieldInfo { fun_borrow: String, fun_borrow_mut: String, fun_remove: String, + fun_remove_if_exists: String, fun_exists_with_type: String, fun_exists: String, fun_exists_inner: String, @@ -245,13 +246,18 @@ pub fn add_prelude( } } let mut dynamic_field_instances = vec![]; + let uid_qid = env.uid_qid(); for info in dynamic_field_analysis::get_env_info(env).dynamic_fields() { let (struct_qid, type_inst) = info.0.get_datatype().unwrap(); - if mono_info.is_used_datatype(env, targets, &struct_qid) - && mono_info - .structs - .get(&struct_qid) - .is_some_and(|type_inst_set| type_inst_set.contains(type_inst)) + // For UID types (when function takes &UID directly), always generate instances + // For other types, check if the datatype is used + let is_uid_type = uid_qid.as_ref().is_some_and(|uid| *uid == struct_qid); + if is_uid_type + || (mono_info.is_used_datatype(env, targets, &struct_qid) + && mono_info + .structs + .get(&struct_qid) + .is_some_and(|type_inst_set| type_inst_set.contains(type_inst))) { dynamic_field_instances.push(DynamicFieldInfo::dynamic_field( env, options, info.0, info.1, false, @@ -716,14 +722,23 @@ impl TableImpl { fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { triple_opt - .map(|fun_qid| { - let fun = env.get_function(fun_qid); - format!( - "${}_{}_{}", - fun.module_env.get_name().addr().to_str_radix(16), - fun.module_env.get_name().name().display(fun.symbol_pool()), - fun.get_name_str(), - ) + .and_then(|fun_qid| { + // Check if the function actually exists before trying to format its name + let module_env = env.get_module(fun_qid.module_id); + if module_env + .into_functions() + .any(|f| f.get_id() == fun_qid.id) + { + let fun = env.get_function(fun_qid); + Some(format!( + "${}_{}_{}", + fun.module_env.get_name().addr().to_str_radix(16), + fun.module_env.get_name().name().display(fun.symbol_pool()), + fun.get_name_str(), + )) + } else { + None + } }) .unwrap_or_default() } @@ -763,6 +778,10 @@ impl DynamicFieldInfo { fun_borrow: Self::triple_opt_to_name(env, env.dynamic_field_borrow_qid()), fun_borrow_mut: Self::triple_opt_to_name(env, env.dynamic_field_borrow_mut_qid()), fun_remove: Self::triple_opt_to_name(env, env.dynamic_field_remove_qid()), + fun_remove_if_exists: Self::triple_opt_to_name( + env, + env.dynamic_field_remove_if_exists_qid(), + ), fun_exists_with_type: Self::triple_opt_to_name( env, env.dynamic_field_exists_with_type_qid(), @@ -818,6 +837,10 @@ impl DynamicFieldInfo { env.dynamic_object_field_borrow_mut_qid(), ), fun_remove: Self::triple_opt_to_name(env, env.dynamic_object_field_remove_qid()), + fun_remove_if_exists: Self::triple_opt_to_name( + env, + env.dynamic_object_field_remove_if_exists_qid(), + ), fun_exists_with_type: Self::triple_opt_to_name( env, env.dynamic_object_field_exists_with_type_qid(), @@ -839,14 +862,23 @@ impl DynamicFieldInfo { fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { triple_opt - .map(|fun_qid| { - let fun = env.get_function(fun_qid); - format!( - "${}_{}_{}", - fun.module_env.get_name().addr().to_str_radix(16), - fun.module_env.get_name().name().display(fun.symbol_pool()), - fun.get_name_str(), - ) + .and_then(|fun_qid| { + // Check if the function actually exists before trying to format its name + let module_env = env.get_module(fun_qid.module_id); + if module_env + .into_functions() + .any(|f| f.get_id() == fun_qid.id) + { + let fun = env.get_function(fun_qid); + Some(format!( + "${}_{}_{}", + fun.module_env.get_name().addr().to_str_radix(16), + fun.module_env.get_name().name().display(fun.symbol_pool()), + fun.get_name_str(), + )) + } else { + None + } }) .unwrap_or_default() } diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl b/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl index 7f26c301f3..e3137dda5c 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl +++ b/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl @@ -927,6 +927,23 @@ procedure {:inline 2} {{impl.fun_remove}}{{DF_S}}(m: $Mutation ({{Type}}), k: {{ } {%- endif %} +{%- if impl.fun_remove_if_exists != "" %} +// remove_if_exists: removes the dynamic field if it exists, otherwise no-op +procedure {:inline 2} {{impl.fun_remove_if_exists}}{{DF_S}}(m: $Mutation ({{Type}}), k: {{K}}) returns (v: $1_option_Option'{{instance.1.suffix}}', m': $Mutation({{Type}})) { + var enc_k: int; + var t: {{Type}}; + enc_k := {{ENC}}(k); + t := $Dereference(m); + if (ContainsTable(t->$dynamic_fields{{S}}, enc_k)) { + m' := $UpdateMutation(m, $Update'{{Type}}'_dynamic_fields{{S}}(t, RemoveTable(t->$dynamic_fields{{S}}, enc_k))); + } else { + m' := m; + } + // v is havoc'd - the important effect is the removal from dynamic_fields + havoc v; +} +{%- endif %} + {%- if impl.fun_exists_with_type != "" %} function {:inline} {{impl.fun_exists_with_type}}{{DF_S}}(t: ({{Type}}), k: {{K}}): bool { ContainsTable(t->$dynamic_fields{{S}}, {{ENC}}(k)) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 2639c7f536..65e00c2103 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -14,7 +14,7 @@ use crate::{ use codespan_reporting::diagnostic::{Diagnostic, Label, Severity}; use itertools::Itertools; use move_model::{ - model::{FunctionEnv, GlobalEnv, StructEnv}, + model::{DatatypeId, FunctionEnv, GlobalEnv, QualifiedId, StructEnv}, ty::Type, }; use std::{ @@ -27,6 +27,9 @@ use std::{ pub struct DynamicFieldInfo { dynamic_field_mappings: BTreeMap>, uid_info: BTreeMap, + /// For each UID parameter index, stores the actual object types resolved from call sites. + /// This is populated during inter-procedural analysis when we find all callers. + uid_param_object_types: BTreeMap>, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -64,6 +67,7 @@ impl DynamicFieldInfo { Self { dynamic_field_mappings: BTreeMap::new(), uid_info: BTreeMap::new(), + uid_param_object_types: BTreeMap::new(), } } @@ -73,6 +77,7 @@ impl DynamicFieldInfo { Self { dynamic_field_mappings: BTreeMap::from([(ty, BTreeSet::from([name_value_info]))]), uid_info: BTreeMap::new(), + uid_param_object_types: BTreeMap::new(), } } @@ -102,6 +107,13 @@ impl DynamicFieldInfo { .or_insert_with(BTreeSet::new) .extend(name_value_set.iter().cloned()); } + for (param_idx, obj_types) in other.uid_param_object_types.iter() { + new_info + .uid_param_object_types + .entry(*param_idx) + .or_insert_with(BTreeSet::new) + .extend(obj_types.iter().cloned()); + } new_info } @@ -109,6 +121,7 @@ impl DynamicFieldInfo { pub fn instantiate(&self, type_inst: &[Type]) -> Self { Self { uid_info: BTreeMap::new(), + uid_param_object_types: BTreeMap::new(), dynamic_field_mappings: self .dynamic_field_mappings .iter() @@ -141,6 +154,56 @@ impl DynamicFieldInfo { pub fn iter_union(info: impl Iterator) -> Self { info.fold(Self::new(), |acc, info| acc.union(&info)) } + + /// Substitute a type in dynamic_field_mappings keys with another type. + /// Used to replace UID type with actual object type when resolving UID parameters. + pub fn substitute_object_type(&self, from_type: &Type, to_type: &Type) -> Self { + Self { + uid_info: self.uid_info.clone(), + uid_param_object_types: self.uid_param_object_types.clone(), + dynamic_field_mappings: self + .dynamic_field_mappings + .iter() + .map(|(ty, name_value_set)| { + let new_ty = if ty == from_type { + to_type.clone() + } else { + ty.clone() + }; + (new_ty, name_value_set.clone()) + }) + .collect(), + } + } + + /// Get the resolved object types for a UID parameter + pub fn get_uid_param_object_types(&self, param_idx: usize) -> Option<&BTreeSet> { + self.uid_param_object_types.get(¶m_idx) + } + + /// Add a resolved object type for a UID parameter + pub fn add_uid_param_object_type(&mut self, param_idx: usize, obj_type: Type) { + self.uid_param_object_types + .entry(param_idx) + .or_insert_with(BTreeSet::new) + .insert(obj_type); + } + + /// Check if the given temp index is a UID parameter (self-referential in uid_info) + pub fn is_uid_parameter(&self, temp_idx: usize) -> bool { + self.uid_info + .get(&temp_idx) + .map(|(obj_local, _)| *obj_local == temp_idx) + .unwrap_or(false) + } + + /// Get the object type for a UID parameter + pub fn get_uid_param_type(&self, temp_idx: usize) -> Option<&Type> { + self.uid_info + .get(&temp_idx) + .filter(|(obj_local, _)| *obj_local == temp_idx) + .map(|(_, ty)| ty) + } } /// Get the information computed by this analysis for the global environment @@ -267,6 +330,9 @@ fn collect_dynamic_field_info( .flatten() .collect_vec(); + // Get UID QID for filtering in generic functions + let uid_qid = builder.fun_env.module_env.env.uid_qid(); + // compute map of temp index that load object ids to type of object let uid_info = compute_uid_info(&builder.get_target(), targets, &builder.data.code); @@ -302,10 +368,20 @@ fn collect_dynamic_field_info( return None; } + // For generic functions with UID params, skip self-referential entries to + // trigger "UID object type not found" error (prevents bad Boogie type generation). + // For non-generic functions, allow UID params. + let has_type_params = !builder.fun_env.get_type_parameters().is_empty(); + if dynamic_field_name_value_fun_qids.contains(&callee_id) { - if let Some((_, obj_type)) = - get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) - { + if let Some((_, obj_type)) = get_uid_object_type_impl( + &uid_info, + &alias_info, + srcs[0], + offset, + has_type_params, + uid_qid, + ) { return Some(DynamicFieldInfo::singleton( obj_type.clone(), NameValueInfo::NameValue { @@ -320,9 +396,14 @@ fn collect_dynamic_field_info( } if dynamic_field_name_value_fun_mut_qids.contains(&callee_id) { - if let Some((_, obj_type)) = - get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) - { + if let Some((_, obj_type)) = get_uid_object_type_impl( + &uid_info, + &alias_info, + srcs[0], + offset, + has_type_params, + uid_qid, + ) { return Some(DynamicFieldInfo::singleton( obj_type.clone(), NameValueInfo::NameValue { @@ -337,9 +418,14 @@ fn collect_dynamic_field_info( } if dynamic_field_name_only_fun_qids.contains(&callee_id) { - if let Some((_, obj_type)) = - get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) - { + if let Some((_, obj_type)) = get_uid_object_type_impl( + &uid_info, + &alias_info, + srcs[0], + offset, + has_type_params, + uid_qid, + ) { return Some(DynamicFieldInfo::singleton( obj_type.clone(), NameValueInfo::NameOnly(type_inst[0].clone()), @@ -364,21 +450,54 @@ fn collect_dynamic_field_info( return None; } - let info = targets + let callee_data = targets .get_data(fun_id_with_info, &FunctionVariant::Baseline) - .map(|data| get_fun_info(data)) .expect(&format!( "callee `{}` of `{}` was filtered out", func_env.get_full_name_str(), builder.fun_env.get_full_name_str() )); - Some(info.instantiate(type_inst)) + let callee_info = get_fun_info(callee_data); + + // Substitute UID parameter types with actual object types from caller context + let substituted_info = + substitute_uid_params(callee_info, &uid_info, &alias_info, srcs, offset); + + Some(substituted_info.instantiate(type_inst)) } _ => None, }, )); - info.uid_info = uid_info; + info.uid_info = uid_info.clone(); + + // First pass: identify parent objects whose UIDs are passed to functions with UID parameters. + // For any df::* operations on UIDs from these parents, we should use UID type to match callee. + let mut parents_with_uid_param_calls: BTreeSet = BTreeSet::new(); + for bc in builder.data.code.iter() { + if let Bytecode::Call(_, _, Operation::Function(module_id, callee_fun_id, _), srcs, _) = bc + { + let callee_id = module_id.qualified(*callee_fun_id); + if callee_id == builder.fun_env.get_qualified_id() { + continue; + } + if let Some(callee_data) = targets.get_data(&callee_id, &FunctionVariant::Baseline) { + let callee_info = get_fun_info(callee_data); + // Check if callee has any UID params (self-referential entries) + for (param_idx, (obj_local, _)) in &callee_info.uid_info { + if param_idx == obj_local && *param_idx < srcs.len() { + // This callee has a UID param at param_idx + // Find the parent object for the UID being passed + let uid_temp = srcs[*param_idx]; + if let Some((parent, _)) = uid_info.get(&uid_temp) { + // Mark this parent as having UIDs passed to UID-param functions + parents_with_uid_param_calls.insert(*parent); + } + } + } + } + } + } let code = std::mem::take(&mut builder.data.code); for (offset, bc) in code.into_iter().enumerate() { @@ -390,16 +509,41 @@ fn collect_dynamic_field_info( mut srcs, aa, ) if all_dynamic_field_fun_qids.contains(&module_id.qualified(fun_id)) => { + // For direct df::* calls, add the object type from uid_info. if let Some((obj_local, obj_type)) = - get_uid_object_type(&info.uid_info, &alias_info, srcs[0], offset) + get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) { - srcs[0] = *obj_local; - if !dynamic_field_name_value_fun_mut_qids.contains(&module_id.qualified(fun_id)) - && builder.get_local_type(srcs[0]).is_mutable_reference() - { - srcs[0] = builder.emit_let_read_ref(srcs[0]); + // Check if the parent object (or any alias) has UIDs passed to UID-param fns. + // If so, use UID type for consistency between caller's df::* checks and + // callee's df::* operations. + let use_uid_type = parents_with_uid_param_calls.contains(obj_local) + || parents_with_uid_param_calls.iter().any(|parent| { + alias_info.values().any(|state| { + let obj_local_defs = + ReachingDefProcessor::all_aliases(state, obj_local); + let parent_defs = ReachingDefProcessor::all_aliases(state, parent); + !obj_local_defs.is_disjoint(&parent_defs) + || obj_local_defs.contains(parent) + || parent_defs.contains(obj_local) + }) + }); + + if use_uid_type { + // Use UID type for consistency with callee that has UID param + if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { + type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); + } + } else { + // Normal case: use parent object type + srcs[0] = *obj_local; + if !dynamic_field_name_value_fun_mut_qids + .contains(&module_id.qualified(fun_id)) + && builder.get_local_type(srcs[0]).is_mutable_reference() + { + srcs[0] = builder.emit_let_read_ref(srcs[0]); + } + type_inst.push(obj_type.clone()); } - type_inst.push(obj_type.clone()); } builder.emit(Bytecode::Call( attr_id, @@ -416,14 +560,85 @@ fn collect_dynamic_field_info( info } +/// Add actual object type entries to callee's DynamicFieldInfo based on caller context. +/// When a function has UID parameters and is called with a UID from a known object, +/// we add entries for the actual object type (in addition to the UID type entries). +/// This ensures the Boogie prelude generates procedures for both the callee's UID type +/// and the caller's actual object type. +fn substitute_uid_params( + callee_info: &DynamicFieldInfo, + caller_uid_info: &BTreeMap, + alias_info: &BTreeMap, + srcs: &[usize], + offset: usize, +) -> DynamicFieldInfo { + let mut result = callee_info.clone(); + + // For each UID parameter in callee (self-referential entries where temp_idx == obj_local) + for (temp_idx, (obj_local, uid_type)) in &callee_info.uid_info { + // Check if this is a self-referential entry (UID parameter) + if temp_idx == obj_local && *temp_idx < srcs.len() { + // This is a UID parameter at position temp_idx + // Look up the corresponding source in caller's context + let src_idx = srcs[*temp_idx]; + if let Some((_, actual_obj_type)) = + get_uid_object_type(caller_uid_info, alias_info, src_idx, offset) + { + // Add entries for the actual object type (keep UID entries too) + // This creates mappings for both UID and MyObject, so Boogie prelude + // generates procedures for both types. + if let Some(uid_name_values) = callee_info.dynamic_field_mappings.get(uid_type) { + result + .dynamic_field_mappings + .entry(actual_obj_type.clone()) + .or_insert_with(BTreeSet::new) + .extend(uid_name_values.iter().cloned()); + } + } + } + } + + result +} + /// Computes a mapping from temporary indices to the objects and types of objects they reference fn compute_uid_info( fun_target: &FunctionTarget, targets: &FunctionTargetsHolder, code: &[Bytecode], ) -> BTreeMap { - code.iter() - // First collect all potential UID type mappings, grouped by temporary index + let env = fun_target.global_env(); + + // First, collect UID parameters - when a function accepts UID directly as a parameter + // For UID parameters, we store them with obj_local == param_idx (self-referential) + // to mark them as UID parameters. The actual object type will be resolved at call sites. + let uid_params: BTreeMap = if let Some(uid_qid) = env.uid_qid() { + (0..fun_target.get_parameter_count()) + .filter_map(|idx| { + let ty = fun_target.get_local_type(idx); + let inner_ty = ty.skip_reference(); + if let Some((qid, tys)) = inner_ty.get_datatype() { + if qid == uid_qid { + // UID parameter: obj_local is the parameter itself (self-referential marker), + // obj_type is the UID type. At call sites, this will be substituted with + // the actual parent object type via substitute_uid_params. + return Some(( + idx, + (idx, Type::Datatype(qid.module_id, qid.id, tys.to_vec())), + )); + } + } + None + }) + .collect() + } else { + BTreeMap::new() + }; + + // Then collect from bytecode (field accesses and function calls) + let from_bytecode: BTreeMap = code + .iter() + // Collect all potential UID type mappings, grouped by temporary index .filter_map(|bc| match bc { Bytecode::Call( attr_id, @@ -510,7 +725,38 @@ fn compute_uid_info( None } }) - .collect() + .collect(); + + // Merge: bytecode-derived info takes precedence over parameter-derived info + let mut result = uid_params; + result.extend(from_bytecode); + + // Propagate UID info through FreezeRef and ReadRef operations. + // Note: EliminateImmRefsProcessor runs before DynamicFieldAnalysisProcessor and converts + // FreezeRef to ReadRef, so we need to track both. + // Also track Assign operations which can propagate references. + for bc in code { + match bc { + Bytecode::Call(_, dests, Operation::FreezeRef, srcs, _) + | Bytecode::Call(_, dests, Operation::ReadRef, srcs, _) => { + if !dests.is_empty() && !srcs.is_empty() { + // If the source is a UID, propagate the mapping to the destination + if let Some((obj_local, obj_type)) = result.get(&srcs[0]).cloned() { + result.entry(dests[0]).or_insert((obj_local, obj_type)); + } + } + } + Bytecode::Assign(_, dest, src, _) => { + // Track assignments that propagate UID references + if let Some((obj_local, obj_type)) = result.get(src).cloned() { + result.entry(*dest).or_insert((obj_local, obj_type)); + } + } + _ => {} + } + } + + result } /// Checks if a field access at the given offset is accessing the single UID field. @@ -551,17 +797,54 @@ fn get_uid_object_type<'a>( temp_idx: usize, off: usize, ) -> Option<&'a (usize, Type)> { + get_uid_object_type_impl(uid_info, alias_info, temp_idx, off, false, None) +} + +/// Get UID object type, optionally skipping UID parameters (self-referential entries). +/// When skip_uid_params is true, entries where obj_type is UID are skipped. +/// This is used in bytecode transformation to avoid adding UID type for UID parameters +/// in generic functions (prevents recursive Boogie datatype generation). +fn get_uid_object_type_impl<'a>( + uid_info: &'a BTreeMap, + alias_info: &'a BTreeMap, + temp_idx: usize, + off: usize, + skip_uid_params: bool, + uid_qid: Option>, +) -> Option<&'a (usize, Type)> { + // Helper to check if an entry should be skipped + // For generic functions, skip entries where obj_type is UID itself + // (these came from UID parameters either directly or through propagation) + let should_skip = |obj_type: &Type| -> bool { + if !skip_uid_params { + return false; + } + if let Some(uid_qid) = uid_qid { + if let Some((qid, _)) = obj_type.get_datatype() { + return qid == uid_qid; + } + } + false + }; + // First check if temp_idx is directly in uid_info - uid_info.get(&temp_idx).or_else(|| { - // Otherwise, check if we can find it through alias information - alias_info.get(&(off as u16)).and_then(|state| { - ReachingDefProcessor::all_aliases(state, &temp_idx) - .iter() - .filter_map(|alias| uid_info.get(alias)) - .exactly_one() - .ok() + uid_info + .get(&temp_idx) + .filter(|(_, obj_type)| !should_skip(obj_type)) + .or_else(|| { + // Otherwise, check if we can find it through alias information + alias_info.get(&(off as u16)).and_then(|state| { + ReachingDefProcessor::all_aliases(state, &temp_idx) + .iter() + .filter_map(|alias| { + uid_info + .get(alias) + .filter(|(_, obj_type)| !should_skip(obj_type)) + }) + .exactly_one() + .ok() + }) }) - }) } pub struct DynamicFieldAnalysisProcessor(); @@ -605,8 +888,49 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } fn finalize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { - // Collect and combine all functions' dynamic field info - let combined_info = DynamicFieldInfo::iter_union(targets.specs().filter_map(|fun_id| { + // Phase 1: Collect UID parameter object types from all call sites + let (uid_param_types, _call_site_info) = collect_uid_param_info(env, targets); + + // Phase 2: Update callee's DynamicFieldInfo with resolved object types + // This ensures the Boogie prelude generates procedures for both UID and MyObject + for (callee_id, param_types) in &uid_param_types { + if let Some(data) = targets.get_data_mut(callee_id, &FunctionVariant::Baseline) { + if let Some(callee_info) = data.annotations.get::().cloned() { + let mut updated_info = callee_info.clone(); + + for (param_idx, obj_types) in param_types { + if let Some((obj_local, uid_type)) = callee_info.uid_info.get(param_idx) { + if obj_local == param_idx { + // This is a UID parameter + if let Some(name_values) = + callee_info.dynamic_field_mappings.get(uid_type) + { + for obj_type in obj_types { + // Add mappings for the actual object type + updated_info + .dynamic_field_mappings + .entry(obj_type.clone()) + .or_insert_with(BTreeSet::new) + .extend(name_values.iter().cloned()); + + updated_info + .uid_param_object_types + .entry(*param_idx) + .or_insert_with(BTreeSet::new) + .insert(obj_type.clone()); + } + } + } + } + } + + data.annotations.set(updated_info, true); + } + } + } + + // Phase 4: Collect and combine all functions' dynamic field info + let combined_info = DynamicFieldInfo::iter_union(targets.get_funs().filter_map(|fun_id| { targets .get_data(&fun_id, &FunctionVariant::Baseline) .and_then(|data| { @@ -624,3 +948,119 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { "dynamic_field_analysis_processor".to_string() } } + +/// Collect UID parameter info from all call sites. +/// Returns: +/// 1. A map from callee function ID to a map from parameter index to set of actual object types +/// 2. A map from caller function ID to a list of (offset, param_idx, obj_local) for call site transforms +fn collect_uid_param_info( + env: &GlobalEnv, + targets: &FunctionTargetsHolder, +) -> ( + BTreeMap< + move_model::model::QualifiedId, + BTreeMap>, + >, + BTreeMap, Vec<(usize, usize, usize)>>, +) { + use move_model::model::QualifiedId; + + let mut param_types: BTreeMap< + QualifiedId, + BTreeMap>, + > = BTreeMap::new(); + let mut call_sites: BTreeMap< + QualifiedId, + Vec<(usize, usize, usize)>, + > = BTreeMap::new(); + + // Scan all functions to find calls to functions with UID parameters + for fun_id in targets.get_funs() { + if let Some(data) = targets.get_data(&fun_id, &FunctionVariant::Baseline) { + let fun_env = env.get_function(fun_id); + if fun_env.is_native() || fun_env.is_intrinsic() { + continue; + } + + // Get the caller's uid_info + let caller_info = match data.annotations.get::() { + Some(info) => info, + None => continue, + }; + + // Compute alias info for the caller + let alias_info = ReachingDefProcessor::analyze_reaching_definitions(&fun_env, data); + + // Scan the caller's bytecode for calls + for (offset, bc) in data.code.iter().enumerate() { + if let Bytecode::Call( + _, + _, + Operation::Function(module_id, callee_fun_id, _type_inst), + srcs, + _, + ) = bc + { + let callee_id = module_id.qualified(*callee_fun_id); + + // Skip recursive calls + if callee_id == fun_id { + continue; + } + + // Get the callee's info to check if it has UID parameters + let callee_data = match targets.get_data(&callee_id, &FunctionVariant::Baseline) + { + Some(d) => d, + None => continue, + }; + let callee_info = match callee_data.annotations.get::() { + Some(info) => info, + None => continue, + }; + + // Check each UID parameter in callee + for (param_idx, (obj_local, _uid_type)) in &callee_info.uid_info { + // Check if this is a self-referential entry (UID parameter) + if param_idx == obj_local && *param_idx < srcs.len() { + // This callee has a UID parameter at param_idx + // Look up the corresponding source in caller's context + let src_idx = srcs[*param_idx]; + if let Some((caller_obj_local, actual_obj_type)) = get_uid_object_type( + &caller_info.uid_info, + &alias_info, + src_idx, + offset, + ) { + // Skip if the actual type is also self-referential (UID param in caller) + if let Some((local, _)) = caller_info.uid_info.get(&src_idx) { + if *local == src_idx { + // Caller also has a UID parameter, skip + continue; + } + } + + // Add the actual object type + param_types + .entry(callee_id) + .or_insert_with(BTreeMap::new) + .entry(*param_idx) + .or_insert_with(BTreeSet::new) + .insert(actual_obj_type.clone()); + + // Add call site transform info + call_sites.entry(fun_id).or_insert_with(Vec::new).push(( + offset, + *param_idx, + *caller_obj_local, + )); + } + } + } + } + } + } + } + + (param_types, call_sites) +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param.ok.move new file mode 100644 index 0000000000..17a0dc870e --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param.ok.move @@ -0,0 +1,24 @@ +module 0x42::foo; + +use prover::prover::{requires, ensures}; +use sui::dynamic_field as df; + +public struct WhitelistKey has copy, store, drop { + address: address, +} + +public struct MyObject has key { + id: UID, +} + +public fun add_whitelist_address(uid: &mut UID, addr: address) { + df::add(uid, WhitelistKey { address: addr }, true); +} + +#[spec(prove)] +fun add_whitelist_spec(obj: &mut MyObject, addr: address) { + requires(!df::exists_with_type(&obj.id, WhitelistKey { address: addr })); + add_whitelist_address(&mut obj.id, addr); + ensures(df::exists_with_type(&obj.id, WhitelistKey { address: addr })); + ensures(*df::borrow(&obj.id, WhitelistKey { address: addr }) == true); +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_exists.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_exists.ok.move new file mode 100644 index 0000000000..b6064976e7 --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_exists.ok.move @@ -0,0 +1,20 @@ +module 0x42::foo; + +use sui::dynamic_field as df; + +public struct WhitelistKey has copy, store, drop { + address: address, +} + +public struct MyObject has key { + id: UID, +} + +public fun in_whitelist(uid: &UID, addr: address): bool { + df::exists_with_type(uid, WhitelistKey { address: addr }) +} + +#[spec(prove)] +fun check_in_whitelist(obj: &MyObject, addr: address): bool { + in_whitelist(&obj.id, addr) +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move new file mode 100644 index 0000000000..2146905ed2 --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move @@ -0,0 +1,20 @@ +module 0x42::foo; + +use sui::dynamic_field as df; + +public struct WhitelistKey has copy, store, drop { + address: address, +} + +public struct MyObject has key { + id: UID, +} + +public fun remove_whitelist_address(uid: &mut UID, addr: address) { + df::remove_if_exists(uid, WhitelistKey { address: addr }); +} + +#[spec(prove)] +fun remove_whitelist_spec(obj: &mut MyObject, addr: address) { + remove_whitelist_address(&mut obj.id, addr); +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_target.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_target.ok.move new file mode 100644 index 0000000000..6ca2f24aa4 --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_target.ok.move @@ -0,0 +1,42 @@ +module 0x42::foo; + +use sui::dynamic_field as df; +use prover::prover::asserts; + +public struct WhitelistKey has copy, store, drop { + address: address, +} + +public struct AllowAllKey has copy, store, drop {} + +public struct MyObject has key { + id: UID, +} + +public fun add_whitelist_address(uid: &mut UID, addr: address) { + df::add(uid, WhitelistKey { address: addr }, true); +} + +public fun allow_all(uid: &mut UID) { + df::add(uid, AllowAllKey {}, true); +} + +public fun whitelist_key(addr: address): WhitelistKey { + WhitelistKey { address: addr } +} + +public fun allow_all_key(): AllowAllKey { + AllowAllKey {} +} + +#[spec(prove, target = add_whitelist_address)] +fun add_whitelist_address_spec(uid: &mut UID, addr: address) { + asserts(!df::exists_with_type(uid, whitelist_key(addr))); + add_whitelist_address(uid, addr); +} + +#[spec(prove, target = allow_all)] +fun allow_all_spec(uid: &mut UID) { + asserts(!df::exists_with_type(uid, allow_all_key())); + allow_all(uid); +} diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap new file mode 100644 index 0000000000..c4de20f8bf --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap @@ -0,0 +1,6 @@ +--- +source: crates/sui-prover/tests/integration.rs +assertion_line: 297 +expression: output +--- +Verification successful diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap new file mode 100644 index 0000000000..c4de20f8bf --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap @@ -0,0 +1,6 @@ +--- +source: crates/sui-prover/tests/integration.rs +assertion_line: 297 +expression: output +--- +Verification successful diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap new file mode 100644 index 0000000000..c4de20f8bf --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap @@ -0,0 +1,6 @@ +--- +source: crates/sui-prover/tests/integration.rs +assertion_line: 297 +expression: output +--- +Verification successful diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap new file mode 100644 index 0000000000..c4de20f8bf --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap @@ -0,0 +1,6 @@ +--- +source: crates/sui-prover/tests/integration.rs +assertion_line: 297 +expression: output +--- +Verification successful From 58462acee803ace4ace09dbce326f6d201c5a819 Mon Sep 17 00:00:00 2001 From: Andrii Date: Tue, 24 Feb 2026 17:48:25 +0200 Subject: [PATCH 02/21] feat: cleanup --- .../src/boogie_backend/lib.rs | 3 - .../src/dynamic_field_analysis.rs | 180 +----------------- 2 files changed, 4 insertions(+), 179 deletions(-) diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs index 3cff38fc30..fe07f2533d 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs @@ -249,8 +249,6 @@ pub fn add_prelude( let uid_qid = env.uid_qid(); for info in dynamic_field_analysis::get_env_info(env).dynamic_fields() { let (struct_qid, type_inst) = info.0.get_datatype().unwrap(); - // For UID types (when function takes &UID directly), always generate instances - // For other types, check if the datatype is used let is_uid_type = uid_qid.as_ref().is_some_and(|uid| *uid == struct_qid); if is_uid_type || (mono_info.is_used_datatype(env, targets, &struct_qid) @@ -723,7 +721,6 @@ impl TableImpl { fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { triple_opt .and_then(|fun_qid| { - // Check if the function actually exists before trying to format its name let module_env = env.get_module(fun_qid.module_id); if module_env .into_functions() diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 65e00c2103..db29dabf1a 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -22,13 +22,10 @@ use std::{ rc::Rc, }; -/// Stores dynamic field type information for a specific function #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct DynamicFieldInfo { dynamic_field_mappings: BTreeMap>, uid_info: BTreeMap, - /// For each UID parameter index, stores the actual object types resolved from call sites. - /// This is populated during inter-procedural analysis when we find all callers. uid_param_object_types: BTreeMap>, } @@ -71,8 +68,6 @@ impl DynamicFieldInfo { } } - /// Creates a DynamicFieldInfo with a single mapping from the given type to a single dynamic field type. - /// This is a convenience method for creating simple dynamic field entries. pub fn singleton(ty: Type, name_value_info: NameValueInfo) -> Self { Self { dynamic_field_mappings: BTreeMap::from([(ty, BTreeSet::from([name_value_info]))]), @@ -97,7 +92,6 @@ impl DynamicFieldInfo { .unique() } - /// union two DynamicFieldTypeInfo pub fn union(&self, other: &Self) -> Self { let mut new_info = self.clone(); for (ty, name_value_set) in other.dynamic_field_mappings.iter() { @@ -117,7 +111,6 @@ impl DynamicFieldInfo { new_info } - /// Create a new DynamicFieldTypeInfo with types instantiated using the given type arguments pub fn instantiate(&self, type_inst: &[Type]) -> Self { Self { uid_info: BTreeMap::new(), @@ -154,64 +147,12 @@ impl DynamicFieldInfo { pub fn iter_union(info: impl Iterator) -> Self { info.fold(Self::new(), |acc, info| acc.union(&info)) } - - /// Substitute a type in dynamic_field_mappings keys with another type. - /// Used to replace UID type with actual object type when resolving UID parameters. - pub fn substitute_object_type(&self, from_type: &Type, to_type: &Type) -> Self { - Self { - uid_info: self.uid_info.clone(), - uid_param_object_types: self.uid_param_object_types.clone(), - dynamic_field_mappings: self - .dynamic_field_mappings - .iter() - .map(|(ty, name_value_set)| { - let new_ty = if ty == from_type { - to_type.clone() - } else { - ty.clone() - }; - (new_ty, name_value_set.clone()) - }) - .collect(), - } - } - - /// Get the resolved object types for a UID parameter - pub fn get_uid_param_object_types(&self, param_idx: usize) -> Option<&BTreeSet> { - self.uid_param_object_types.get(¶m_idx) - } - - /// Add a resolved object type for a UID parameter - pub fn add_uid_param_object_type(&mut self, param_idx: usize, obj_type: Type) { - self.uid_param_object_types - .entry(param_idx) - .or_insert_with(BTreeSet::new) - .insert(obj_type); - } - - /// Check if the given temp index is a UID parameter (self-referential in uid_info) - pub fn is_uid_parameter(&self, temp_idx: usize) -> bool { - self.uid_info - .get(&temp_idx) - .map(|(obj_local, _)| *obj_local == temp_idx) - .unwrap_or(false) - } - - /// Get the object type for a UID parameter - pub fn get_uid_param_type(&self, temp_idx: usize) -> Option<&Type> { - self.uid_info - .get(&temp_idx) - .filter(|(obj_local, _)| *obj_local == temp_idx) - .map(|(_, ty)| ty) - } } -/// Get the information computed by this analysis for the global environment pub fn get_env_info(env: &GlobalEnv) -> Rc { env.get_extension::().unwrap() } -/// Get the information computed by this analysis for a function pub fn get_fun_info(data: &FunctionData) -> &DynamicFieldInfo { data.annotations.get::().unwrap() } @@ -220,7 +161,6 @@ pub fn get_function_return_local_pos(local_idx: usize, code: &[Bytecode]) -> Opt let mut return_pos = None; for bc in code.into_iter() { if return_pos.is_some() { - // if function have few return statments return None; } @@ -239,21 +179,18 @@ pub fn get_function_return_local_pos(local_idx: usize, code: &[Bytecode]) -> Opt return_pos } -/// Collect dynamic field type information from a function's bytecode fn collect_dynamic_field_info( targets: &FunctionTargetsHolder, builder: &mut FunctionDataBuilder, verified_or_inlined: bool, ) -> DynamicFieldInfo { let dynamic_field_name_value_fun_qids = vec![ - // dynamic field operations builder.fun_env.module_env.env.dynamic_field_borrow_qid(), builder .fun_env .module_env .env .dynamic_field_exists_with_type_qid(), - // dynamic object field operations builder .fun_env .module_env @@ -269,7 +206,6 @@ fn collect_dynamic_field_info( .filter_map(|x| x) .collect_vec(); let dynamic_field_name_value_fun_mut_qids = vec![ - // dynamic field operations builder.fun_env.module_env.env.dynamic_field_add_qid(), builder .fun_env @@ -282,7 +218,6 @@ fn collect_dynamic_field_info( .module_env .env .dynamic_field_remove_if_exists_qid(), - // dynamic object field operations builder .fun_env .module_env @@ -308,9 +243,7 @@ fn collect_dynamic_field_info( .filter_map(|x| x) .collect_vec(); let dynamic_field_name_only_fun_qids = vec![ - // dynamic field operations builder.fun_env.module_env.env.dynamic_field_exists_qid(), - // dynamic object field operations builder .fun_env .module_env @@ -330,10 +263,8 @@ fn collect_dynamic_field_info( .flatten() .collect_vec(); - // Get UID QID for filtering in generic functions let uid_qid = builder.fun_env.module_env.env.uid_qid(); - // compute map of temp index that load object ids to type of object let uid_info = compute_uid_info(&builder.get_target(), targets, &builder.data.code); let alias_info = @@ -368,9 +299,6 @@ fn collect_dynamic_field_info( return None; } - // For generic functions with UID params, skip self-referential entries to - // trigger "UID object type not found" error (prevents bad Boogie type generation). - // For non-generic functions, allow UID params. let has_type_params = !builder.fun_env.get_type_parameters().is_empty(); if dynamic_field_name_value_fun_qids.contains(&callee_id) { @@ -445,7 +373,6 @@ fn collect_dynamic_field_info( .env .get_function(*fun_id_with_info); - // native, reachable or intrinsic functions do not access dynamic fields if func_env.is_native() || get_info(&builder.get_target()).reachable { return None; } @@ -459,7 +386,6 @@ fn collect_dynamic_field_info( )); let callee_info = get_fun_info(callee_data); - // Substitute UID parameter types with actual object types from caller context let substituted_info = substitute_uid_params(callee_info, &uid_info, &alias_info, srcs, offset); @@ -471,8 +397,6 @@ fn collect_dynamic_field_info( info.uid_info = uid_info.clone(); - // First pass: identify parent objects whose UIDs are passed to functions with UID parameters. - // For any df::* operations on UIDs from these parents, we should use UID type to match callee. let mut parents_with_uid_param_calls: BTreeSet = BTreeSet::new(); for bc in builder.data.code.iter() { if let Bytecode::Call(_, _, Operation::Function(module_id, callee_fun_id, _), srcs, _) = bc @@ -483,14 +407,10 @@ fn collect_dynamic_field_info( } if let Some(callee_data) = targets.get_data(&callee_id, &FunctionVariant::Baseline) { let callee_info = get_fun_info(callee_data); - // Check if callee has any UID params (self-referential entries) for (param_idx, (obj_local, _)) in &callee_info.uid_info { if param_idx == obj_local && *param_idx < srcs.len() { - // This callee has a UID param at param_idx - // Find the parent object for the UID being passed let uid_temp = srcs[*param_idx]; if let Some((parent, _)) = uid_info.get(&uid_temp) { - // Mark this parent as having UIDs passed to UID-param functions parents_with_uid_param_calls.insert(*parent); } } @@ -509,13 +429,9 @@ fn collect_dynamic_field_info( mut srcs, aa, ) if all_dynamic_field_fun_qids.contains(&module_id.qualified(fun_id)) => { - // For direct df::* calls, add the object type from uid_info. if let Some((obj_local, obj_type)) = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) { - // Check if the parent object (or any alias) has UIDs passed to UID-param fns. - // If so, use UID type for consistency between caller's df::* checks and - // callee's df::* operations. let use_uid_type = parents_with_uid_param_calls.contains(obj_local) || parents_with_uid_param_calls.iter().any(|parent| { alias_info.values().any(|state| { @@ -529,12 +445,10 @@ fn collect_dynamic_field_info( }); if use_uid_type { - // Use UID type for consistency with callee that has UID param if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); } } else { - // Normal case: use parent object type srcs[0] = *obj_local; if !dynamic_field_name_value_fun_mut_qids .contains(&module_id.qualified(fun_id)) @@ -560,11 +474,6 @@ fn collect_dynamic_field_info( info } -/// Add actual object type entries to callee's DynamicFieldInfo based on caller context. -/// When a function has UID parameters and is called with a UID from a known object, -/// we add entries for the actual object type (in addition to the UID type entries). -/// This ensures the Boogie prelude generates procedures for both the callee's UID type -/// and the caller's actual object type. fn substitute_uid_params( callee_info: &DynamicFieldInfo, caller_uid_info: &BTreeMap, @@ -574,19 +483,12 @@ fn substitute_uid_params( ) -> DynamicFieldInfo { let mut result = callee_info.clone(); - // For each UID parameter in callee (self-referential entries where temp_idx == obj_local) for (temp_idx, (obj_local, uid_type)) in &callee_info.uid_info { - // Check if this is a self-referential entry (UID parameter) if temp_idx == obj_local && *temp_idx < srcs.len() { - // This is a UID parameter at position temp_idx - // Look up the corresponding source in caller's context let src_idx = srcs[*temp_idx]; if let Some((_, actual_obj_type)) = get_uid_object_type(caller_uid_info, alias_info, src_idx, offset) { - // Add entries for the actual object type (keep UID entries too) - // This creates mappings for both UID and MyObject, so Boogie prelude - // generates procedures for both types. if let Some(uid_name_values) = callee_info.dynamic_field_mappings.get(uid_type) { result .dynamic_field_mappings @@ -601,7 +503,6 @@ fn substitute_uid_params( result } -/// Computes a mapping from temporary indices to the objects and types of objects they reference fn compute_uid_info( fun_target: &FunctionTarget, targets: &FunctionTargetsHolder, @@ -609,9 +510,6 @@ fn compute_uid_info( ) -> BTreeMap { let env = fun_target.global_env(); - // First, collect UID parameters - when a function accepts UID directly as a parameter - // For UID parameters, we store them with obj_local == param_idx (self-referential) - // to mark them as UID parameters. The actual object type will be resolved at call sites. let uid_params: BTreeMap = if let Some(uid_qid) = env.uid_qid() { (0..fun_target.get_parameter_count()) .filter_map(|idx| { @@ -619,9 +517,6 @@ fn compute_uid_info( let inner_ty = ty.skip_reference(); if let Some((qid, tys)) = inner_ty.get_datatype() { if qid == uid_qid { - // UID parameter: obj_local is the parameter itself (self-referential marker), - // obj_type is the UID type. At call sites, this will be substituted with - // the actual parent object type via substitute_uid_params. return Some(( idx, (idx, Type::Datatype(qid.module_id, qid.id, tys.to_vec())), @@ -635,10 +530,8 @@ fn compute_uid_info( BTreeMap::new() }; - // Then collect from bytecode (field accesses and function calls) let from_bytecode: BTreeMap = code .iter() - // Collect all potential UID type mappings, grouped by temporary index .filter_map(|bc| match bc { Bytecode::Call( attr_id, @@ -705,7 +598,6 @@ fn compute_uid_info( .into_group_map() .into_iter() .filter_map(|(temp_idx, types)| { - // Report errors if there are duplicates if types.len() == 1 { Some((temp_idx, (types[0].0.clone(), types[0].1.clone()))) } else { @@ -727,27 +619,20 @@ fn compute_uid_info( }) .collect(); - // Merge: bytecode-derived info takes precedence over parameter-derived info let mut result = uid_params; result.extend(from_bytecode); - // Propagate UID info through FreezeRef and ReadRef operations. - // Note: EliminateImmRefsProcessor runs before DynamicFieldAnalysisProcessor and converts - // FreezeRef to ReadRef, so we need to track both. - // Also track Assign operations which can propagate references. for bc in code { match bc { Bytecode::Call(_, dests, Operation::FreezeRef, srcs, _) | Bytecode::Call(_, dests, Operation::ReadRef, srcs, _) => { if !dests.is_empty() && !srcs.is_empty() { - // If the source is a UID, propagate the mapping to the destination if let Some((obj_local, obj_type)) = result.get(&srcs[0]).cloned() { result.entry(dests[0]).or_insert((obj_local, obj_type)); } } } Bytecode::Assign(_, dest, src, _) => { - // Track assignments that propagate UID references if let Some((obj_local, obj_type)) = result.get(src).cloned() { result.entry(*dest).or_insert((obj_local, obj_type)); } @@ -759,10 +644,6 @@ fn compute_uid_info( result } -/// Checks if a field access at the given offset is accessing the single UID field. -/// Returns true if: -/// - The struct has the `key` ability and the offset is 0, OR -/// - The offset matches the single UID field offset fn is_uid_field_access(struct_env: &StructEnv<'_>, offset: usize) -> bool { (struct_env.get_abilities().has_key() && offset == 0) || Some(offset) == single_uid_field_offset(struct_env) @@ -800,10 +681,6 @@ fn get_uid_object_type<'a>( get_uid_object_type_impl(uid_info, alias_info, temp_idx, off, false, None) } -/// Get UID object type, optionally skipping UID parameters (self-referential entries). -/// When skip_uid_params is true, entries where obj_type is UID are skipped. -/// This is used in bytecode transformation to avoid adding UID type for UID parameters -/// in generic functions (prevents recursive Boogie datatype generation). fn get_uid_object_type_impl<'a>( uid_info: &'a BTreeMap, alias_info: &'a BTreeMap, @@ -812,9 +689,6 @@ fn get_uid_object_type_impl<'a>( skip_uid_params: bool, uid_qid: Option>, ) -> Option<&'a (usize, Type)> { - // Helper to check if an entry should be skipped - // For generic functions, skip entries where obj_type is UID itself - // (these came from UID parameters either directly or through propagation) let should_skip = |obj_type: &Type| -> bool { if !skip_uid_params { return false; @@ -827,12 +701,10 @@ fn get_uid_object_type_impl<'a>( false }; - // First check if temp_idx is directly in uid_info uid_info .get(&temp_idx) .filter(|(_, obj_type)| !should_skip(obj_type)) .or_else(|| { - // Otherwise, check if we can find it through alias information alias_info.get(&(off as u16)).and_then(|state| { ReachingDefProcessor::all_aliases(state, &temp_idx) .iter() @@ -876,7 +748,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { let mut builder = FunctionDataBuilder::new(fun_env, data); - // Collect the dynamic field info let info = collect_dynamic_field_info(targets, &mut builder, info.verified || info.inlined); builder @@ -888,11 +759,8 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } fn finalize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { - // Phase 1: Collect UID parameter object types from all call sites - let (uid_param_types, _call_site_info) = collect_uid_param_info(env, targets); + let uid_param_types = collect_uid_param_info(env, targets); - // Phase 2: Update callee's DynamicFieldInfo with resolved object types - // This ensures the Boogie prelude generates procedures for both UID and MyObject for (callee_id, param_types) in &uid_param_types { if let Some(data) = targets.get_data_mut(callee_id, &FunctionVariant::Baseline) { if let Some(callee_info) = data.annotations.get::().cloned() { @@ -901,12 +769,10 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { for (param_idx, obj_types) in param_types { if let Some((obj_local, uid_type)) = callee_info.uid_info.get(param_idx) { if obj_local == param_idx { - // This is a UID parameter if let Some(name_values) = callee_info.dynamic_field_mappings.get(uid_type) { for obj_type in obj_types { - // Add mappings for the actual object type updated_info .dynamic_field_mappings .entry(obj_type.clone()) @@ -929,7 +795,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } } - // Phase 4: Collect and combine all functions' dynamic field info let combined_info = DynamicFieldInfo::iter_union(targets.get_funs().filter_map(|fun_id| { targets .get_data(&fun_id, &FunctionVariant::Baseline) @@ -940,7 +805,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { }) })); - // Set the combined info in the environment env.set_extension(combined_info); } @@ -949,32 +813,15 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } } -/// Collect UID parameter info from all call sites. -/// Returns: -/// 1. A map from callee function ID to a map from parameter index to set of actual object types -/// 2. A map from caller function ID to a list of (offset, param_idx, obj_local) for call site transforms fn collect_uid_param_info( env: &GlobalEnv, targets: &FunctionTargetsHolder, -) -> ( - BTreeMap< - move_model::model::QualifiedId, - BTreeMap>, - >, - BTreeMap, Vec<(usize, usize, usize)>>, -) { - use move_model::model::QualifiedId; - +) -> BTreeMap, BTreeMap>> { let mut param_types: BTreeMap< QualifiedId, BTreeMap>, > = BTreeMap::new(); - let mut call_sites: BTreeMap< - QualifiedId, - Vec<(usize, usize, usize)>, - > = BTreeMap::new(); - // Scan all functions to find calls to functions with UID parameters for fun_id in targets.get_funs() { if let Some(data) = targets.get_data(&fun_id, &FunctionVariant::Baseline) { let fun_env = env.get_function(fun_id); @@ -982,16 +829,13 @@ fn collect_uid_param_info( continue; } - // Get the caller's uid_info let caller_info = match data.annotations.get::() { Some(info) => info, None => continue, }; - // Compute alias info for the caller let alias_info = ReachingDefProcessor::analyze_reaching_definitions(&fun_env, data); - // Scan the caller's bytecode for calls for (offset, bc) in data.code.iter().enumerate() { if let Bytecode::Call( _, @@ -1003,12 +847,10 @@ fn collect_uid_param_info( { let callee_id = module_id.qualified(*callee_fun_id); - // Skip recursive calls if callee_id == fun_id { continue; } - // Get the callee's info to check if it has UID parameters let callee_data = match targets.get_data(&callee_id, &FunctionVariant::Baseline) { Some(d) => d, @@ -1019,41 +861,27 @@ fn collect_uid_param_info( None => continue, }; - // Check each UID parameter in callee for (param_idx, (obj_local, _uid_type)) in &callee_info.uid_info { - // Check if this is a self-referential entry (UID parameter) if param_idx == obj_local && *param_idx < srcs.len() { - // This callee has a UID parameter at param_idx - // Look up the corresponding source in caller's context let src_idx = srcs[*param_idx]; - if let Some((caller_obj_local, actual_obj_type)) = get_uid_object_type( + if let Some((_, actual_obj_type)) = get_uid_object_type( &caller_info.uid_info, &alias_info, src_idx, offset, ) { - // Skip if the actual type is also self-referential (UID param in caller) if let Some((local, _)) = caller_info.uid_info.get(&src_idx) { if *local == src_idx { - // Caller also has a UID parameter, skip continue; } } - // Add the actual object type param_types .entry(callee_id) .or_insert_with(BTreeMap::new) .entry(*param_idx) .or_insert_with(BTreeSet::new) .insert(actual_obj_type.clone()); - - // Add call site transform info - call_sites.entry(fun_id).or_insert_with(Vec::new).push(( - offset, - *param_idx, - *caller_obj_local, - )); } } } @@ -1062,5 +890,5 @@ fn collect_uid_param_info( } } - (param_types, call_sites) + param_types } From f5053647ba59fc9aae3c0978cca7e143679607f8 Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 26 Feb 2026 01:36:01 +0200 Subject: [PATCH 03/21] feat: refractor + bpl fix --- .../src/boogie_backend/lib.rs | 148 +++++++----------- .../src/boogie_backend/prelude/native.bpl | 7 +- .../src/mono_analysis.rs | 12 +- 3 files changed, 67 insertions(+), 100 deletions(-) diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs index fe07f2533d..97ef8e662e 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs @@ -519,6 +519,32 @@ pub fn add_prelude( Ok(()) } +fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { + triple_opt + .and_then(|fun_qid| { + let module_env = env.get_module(fun_qid.module_id); + if module_env + .into_functions() + .any(|f| f.get_id() == fun_qid.id) + { + let fun = env.get_function(fun_qid); + Some(format!( + "${}_{}_{}", + fun.module_env.get_name().addr().to_str_radix(16), + fun.module_env.get_name().name().display(fun.symbol_pool()), + fun.get_name_str(), + )) + } else { + println!( + "Warning: function with id {:?} not found in module {:?}", + fun_qid.id, fun_qid.module_id + ); + None + } + }) + .unwrap_or_default() +} + impl QuantifierHelperInfo { fn new(env: &GlobalEnv, info: &PureQuantifierHelperInfo) -> Self { let func_env = env.get_function(info.function); @@ -644,19 +670,19 @@ impl TableImpl { }) .unwrap_or_default() { - Self::triple_opt_to_name(env, env.table_new_qid()) + triple_opt_to_name(env, env.table_new_qid()) } else { "".to_string() }, - fun_add: Self::triple_opt_to_name(env, env.table_add_qid()), - fun_borrow: Self::triple_opt_to_name(env, env.table_borrow_qid()), - fun_borrow_mut: Self::triple_opt_to_name(env, env.table_borrow_mut_qid()), - fun_remove: Self::triple_opt_to_name(env, env.table_remove_qid()), - fun_contains: Self::triple_opt_to_name(env, env.table_contains_qid()), - fun_length: Self::triple_opt_to_name(env, env.table_length_qid()), - fun_is_empty: Self::triple_opt_to_name(env, env.table_is_empty_qid()), - fun_destroy_empty: Self::triple_opt_to_name(env, env.table_destroy_empty_qid()), - fun_drop: Self::triple_opt_to_name(env, env.table_drop_qid()), + fun_add: triple_opt_to_name(env, env.table_add_qid()), + fun_borrow: triple_opt_to_name(env, env.table_borrow_qid()), + fun_borrow_mut: triple_opt_to_name(env, env.table_borrow_mut_qid()), + fun_remove: triple_opt_to_name(env, env.table_remove_qid()), + fun_contains: triple_opt_to_name(env, env.table_contains_qid()), + fun_length: triple_opt_to_name(env, env.table_length_qid()), + fun_is_empty: triple_opt_to_name(env, env.table_is_empty_qid()), + fun_destroy_empty: triple_opt_to_name(env, env.table_destroy_empty_qid()), + fun_drop: triple_opt_to_name(env, env.table_drop_qid()), fun_value_id: "".to_string(), } } @@ -701,44 +727,22 @@ impl TableImpl { }) .unwrap_or_default() { - Self::triple_opt_to_name(env, env.object_table_new_qid()) + triple_opt_to_name(env, env.object_table_new_qid()) } else { "".to_string() }, - fun_add: Self::triple_opt_to_name(env, env.object_table_add_qid()), - fun_borrow: Self::triple_opt_to_name(env, env.object_table_borrow_qid()), - fun_borrow_mut: Self::triple_opt_to_name(env, env.object_table_borrow_mut_qid()), - fun_remove: Self::triple_opt_to_name(env, env.object_table_remove_qid()), - fun_contains: Self::triple_opt_to_name(env, env.object_table_contains_qid()), - fun_length: Self::triple_opt_to_name(env, env.object_table_length_qid()), - fun_is_empty: Self::triple_opt_to_name(env, env.object_table_is_empty_qid()), - fun_destroy_empty: Self::triple_opt_to_name(env, env.object_table_destroy_empty_qid()), + fun_add: triple_opt_to_name(env, env.object_table_add_qid()), + fun_borrow: triple_opt_to_name(env, env.object_table_borrow_qid()), + fun_borrow_mut: triple_opt_to_name(env, env.object_table_borrow_mut_qid()), + fun_remove: triple_opt_to_name(env, env.object_table_remove_qid()), + fun_contains: triple_opt_to_name(env, env.object_table_contains_qid()), + fun_length: triple_opt_to_name(env, env.object_table_length_qid()), + fun_is_empty: triple_opt_to_name(env, env.object_table_is_empty_qid()), + fun_destroy_empty: triple_opt_to_name(env, env.object_table_destroy_empty_qid()), fun_drop: "".to_string(), - fun_value_id: Self::triple_opt_to_name(env, env.object_table_value_id_qid()), + fun_value_id: triple_opt_to_name(env, env.object_table_value_id_qid()), } } - - fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { - triple_opt - .and_then(|fun_qid| { - let module_env = env.get_module(fun_qid.module_id); - if module_env - .into_functions() - .any(|f| f.get_id() == fun_qid.id) - { - let fun = env.get_function(fun_qid); - Some(format!( - "${}_{}_{}", - fun.module_env.get_name().addr().to_str_radix(16), - fun.module_env.get_name().name().display(fun.symbol_pool()), - fun.get_name_str(), - )) - } else { - None - } - }) - .unwrap_or_default() - } } impl DynamicFieldInfo { @@ -771,19 +775,13 @@ impl DynamicFieldInfo { struct_name: boogie_type_suffix_bv(env, tp, bv_flag), insts, key_insts, - fun_add: Self::triple_opt_to_name(env, env.dynamic_field_add_qid()), - fun_borrow: Self::triple_opt_to_name(env, env.dynamic_field_borrow_qid()), - fun_borrow_mut: Self::triple_opt_to_name(env, env.dynamic_field_borrow_mut_qid()), - fun_remove: Self::triple_opt_to_name(env, env.dynamic_field_remove_qid()), - fun_remove_if_exists: Self::triple_opt_to_name( - env, - env.dynamic_field_remove_if_exists_qid(), - ), - fun_exists_with_type: Self::triple_opt_to_name( - env, - env.dynamic_field_exists_with_type_qid(), - ), - fun_exists: Self::triple_opt_to_name(env, env.dynamic_field_exists_qid()), + fun_add: triple_opt_to_name(env, env.dynamic_field_add_qid()), + fun_borrow: triple_opt_to_name(env, env.dynamic_field_borrow_qid()), + fun_borrow_mut: triple_opt_to_name(env, env.dynamic_field_borrow_mut_qid()), + fun_remove: triple_opt_to_name(env, env.dynamic_field_remove_qid()), + fun_remove_if_exists: triple_opt_to_name(env, env.dynamic_field_remove_if_exists_qid()), + fun_exists_with_type: triple_opt_to_name(env, env.dynamic_field_exists_with_type_qid()), + fun_exists: triple_opt_to_name(env, env.dynamic_field_exists_qid()), fun_exists_inner: env .dynamic_field_exists_qid() .map(|fun_qid| { @@ -827,22 +825,19 @@ impl DynamicFieldInfo { struct_name: boogie_type_suffix_bv(env, tp, bv_flag), insts, key_insts, - fun_add: Self::triple_opt_to_name(env, env.dynamic_object_field_add_qid()), - fun_borrow: Self::triple_opt_to_name(env, env.dynamic_object_field_borrow_qid()), - fun_borrow_mut: Self::triple_opt_to_name( - env, - env.dynamic_object_field_borrow_mut_qid(), - ), - fun_remove: Self::triple_opt_to_name(env, env.dynamic_object_field_remove_qid()), - fun_remove_if_exists: Self::triple_opt_to_name( + fun_add: triple_opt_to_name(env, env.dynamic_object_field_add_qid()), + fun_borrow: triple_opt_to_name(env, env.dynamic_object_field_borrow_qid()), + fun_borrow_mut: triple_opt_to_name(env, env.dynamic_object_field_borrow_mut_qid()), + fun_remove: triple_opt_to_name(env, env.dynamic_object_field_remove_qid()), + fun_remove_if_exists: triple_opt_to_name( env, env.dynamic_object_field_remove_if_exists_qid(), ), - fun_exists_with_type: Self::triple_opt_to_name( + fun_exists_with_type: triple_opt_to_name( env, env.dynamic_object_field_exists_with_type_qid(), ), - fun_exists: Self::triple_opt_to_name(env, env.dynamic_object_field_exists_qid()), + fun_exists: triple_opt_to_name(env, env.dynamic_object_field_exists_qid()), fun_exists_inner: env .dynamic_object_field_exists_qid() .map(|fun_qid| { @@ -856,27 +851,4 @@ impl DynamicFieldInfo { .unwrap_or_default(), } } - - fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { - triple_opt - .and_then(|fun_qid| { - // Check if the function actually exists before trying to format its name - let module_env = env.get_module(fun_qid.module_id); - if module_env - .into_functions() - .any(|f| f.get_id() == fun_qid.id) - { - let fun = env.get_function(fun_qid); - Some(format!( - "${}_{}_{}", - fun.module_env.get_name().addr().to_str_radix(16), - fun.module_env.get_name().name().display(fun.symbol_pool()), - fun.get_name_str(), - )) - } else { - None - } - }) - .unwrap_or_default() - } } diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl b/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl index e3137dda5c..10c8d33af2 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl +++ b/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl @@ -932,15 +932,18 @@ procedure {:inline 2} {{impl.fun_remove}}{{DF_S}}(m: $Mutation ({{Type}}), k: {{ procedure {:inline 2} {{impl.fun_remove_if_exists}}{{DF_S}}(m: $Mutation ({{Type}}), k: {{K}}) returns (v: $1_option_Option'{{instance.1.suffix}}', m': $Mutation({{Type}})) { var enc_k: int; var t: {{Type}}; + var val: {{V}}; enc_k := {{ENC}}(k); t := $Dereference(m); if (ContainsTable(t->$dynamic_fields{{S}}, enc_k)) { + val := GetTable(t->$dynamic_fields{{S}}, enc_k); + assume $IsValid{{SV}}(val); m' := $UpdateMutation(m, $Update'{{Type}}'_dynamic_fields{{S}}(t, RemoveTable(t->$dynamic_fields{{S}}, enc_k))); + v := $1_option_Option{{SV}}(MakeVec1(val)); } else { m' := m; + v := $1_option_Option{{SV}}(EmptyVec()); } - // v is havoc'd - the important effect is the removal from dynamic_fields - havoc v; } {%- endif %} diff --git a/crates/move-stackless-bytecode/src/mono_analysis.rs b/crates/move-stackless-bytecode/src/mono_analysis.rs index 9263d67a13..d32a44cbe5 100644 --- a/crates/move-stackless-bytecode/src/mono_analysis.rs +++ b/crates/move-stackless-bytecode/src/mono_analysis.rs @@ -72,16 +72,8 @@ impl MonoInfo { } if dt_qid == &env.option_qid().unwrap() { - // NOTE: We disable this optimization to make extra bpl more flexible. - return targets.has_targeted_extra_bpl(env) - || self.is_used_datatype_helper(env, targets, dt_qid) - || self.is_used_datatype_helper(env, targets, &env.vec_set_qid().unwrap()) - || self.is_used_datatype_helper(env, targets, &env.vec_map_qid().unwrap()) - || self.is_generated_module( - env, - targets, - &vec![env.vec_set_module_id(), env.vec_map_module_id()], - ); + // NOTE: cover all option usages is too complex, so we just return true. + return true; } else if dt_qid == &env.vec_map_entry_qid().unwrap() || dt_qid == &env.vec_map_qid().unwrap() { From d0679d3bdb8b429193c8f3eed81a06228cbe6f83 Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 26 Feb 2026 12:13:10 +0200 Subject: [PATCH 04/21] removed unrelated --- Cargo.lock | 605 +++++++----------- .../src/boogie_backend/lib.rs | 126 ++-- .../src/boogie_backend/prelude/native.bpl | 20 - .../src/mono_analysis.rs | 12 +- .../dynamic_field/uid_param_remove.ok.move | 20 - .../uid_param_remove.ok.move.snap | 6 - 6 files changed, 301 insertions(+), 488 deletions(-) delete mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move delete mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap diff --git a/Cargo.lock b/Cargo.lock index 16fb2ad11b..10a2a76f57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "ar_archive_writer" @@ -139,7 +139,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -150,7 +150,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -167,9 +167,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.0" +version = "1.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" +checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" dependencies = [ "aws-lc-sys", "zeroize", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" +checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" dependencies = [ "cc", "cmake", @@ -258,9 +258,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitmaps" @@ -332,9 +332,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-slice-cast" @@ -350,9 +350,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "camino" @@ -362,9 +362,9 @@ checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" [[package]] name = "cc" -version = "1.2.56" +version = "1.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" dependencies = [ "find-msvc-tools", "jobserver", @@ -386,9 +386,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", "js-sys", @@ -422,9 +422,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.60" +version = "4.5.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e" dependencies = [ "clap_builder", "clap_derive", @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.5.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0" dependencies = [ "anstream", "anstyle", @@ -452,14 +452,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "clap_lex" -version = "1.0.0" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "cmake" @@ -673,7 +673,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -684,7 +684,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -714,9 +714,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.8" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", "serde_core", @@ -765,7 +765,7 @@ dependencies = [ "glob", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -797,7 +797,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -842,7 +842,7 @@ dependencies = [ [[package]] name = "enum-compat-util" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "serde_yaml", ] @@ -914,9 +914,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "fixed-hash" @@ -938,9 +938,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "miniz_oxide", @@ -1002,9 +1002,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1017,9 +1017,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1027,15 +1027,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1044,38 +1044,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1085,6 +1085,7 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", + "pin-utils", "slab", ] @@ -1132,19 +1133,6 @@ dependencies = [ "wasip2", ] -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", - "wasip3", -] - [[package]] name = "glob" version = "0.3.3" @@ -1170,7 +1158,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", "ignore", "walkdir", ] @@ -1394,12 +1382,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.20" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http 1.4.0", "http-body 1.0.1", @@ -1517,12 +1506,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -1606,7 +1589,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -1640,9 +1623,9 @@ checksum = "dc1804bdb6a9784758b200007273a8b84e2b0b0b97a8f1e18e763eceb3e9f98a" [[package]] name = "insta" -version = "1.46.3" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" +checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" dependencies = [ "console", "once_cell", @@ -1727,9 +1710,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.89" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4eacb0641a310445a4c513f2a5e23e19952e269c6a38887254d5f837a305506" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -1737,9 +1720,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -1829,17 +1812,11 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" -version = "0.2.182" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libm" @@ -1853,9 +1830,9 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", "libc", - "redox_syscall 0.7.1", + "redox_syscall 0.7.0", ] [[package]] @@ -1878,9 +1855,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.12.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1930,9 +1907,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" @@ -1976,17 +1953,17 @@ dependencies = [ [[package]] name = "move-abstract-interpreter" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" [[package]] name = "move-abstract-stack" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" [[package]] name = "move-binary-format" version = "0.0.3" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "enum-compat-util", @@ -2002,12 +1979,12 @@ dependencies = [ [[package]] name = "move-borrow-graph" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" [[package]] name = "move-bytecode-source-map" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "bcs", @@ -2023,7 +2000,7 @@ dependencies = [ [[package]] name = "move-bytecode-utils" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "indexmap 2.13.0", @@ -2036,7 +2013,7 @@ dependencies = [ [[package]] name = "move-bytecode-verifier" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "move-abstract-interpreter", "move-abstract-stack", @@ -2052,7 +2029,7 @@ dependencies = [ [[package]] name = "move-bytecode-verifier-meter" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "move-binary-format", "move-core-types", @@ -2062,7 +2039,7 @@ dependencies = [ [[package]] name = "move-command-line-common" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "bcs", @@ -2083,7 +2060,7 @@ dependencies = [ [[package]] name = "move-compiler" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "bcs", @@ -2119,7 +2096,7 @@ dependencies = [ [[package]] name = "move-core-types" version = "0.0.4" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "bcs", @@ -2144,7 +2121,7 @@ dependencies = [ [[package]] name = "move-coverage" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "bcs", @@ -2169,7 +2146,7 @@ dependencies = [ [[package]] name = "move-disassembler" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "bcs", @@ -2190,7 +2167,7 @@ dependencies = [ [[package]] name = "move-docgen" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "clap", @@ -2213,7 +2190,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "codespan-reporting", @@ -2231,7 +2208,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode-syntax" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "hex", @@ -2244,7 +2221,7 @@ dependencies = [ [[package]] name = "move-ir-types" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "hex", "move-command-line-common", @@ -2282,7 +2259,7 @@ dependencies = [ [[package]] name = "move-model" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "codespan", @@ -2307,7 +2284,7 @@ dependencies = [ [[package]] name = "move-model-2" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "bcs", @@ -2331,7 +2308,7 @@ dependencies = [ [[package]] name = "move-package" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "clap", @@ -2368,11 +2345,11 @@ dependencies = [ [[package]] name = "move-proc-macros" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "enum-compat-util", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -2414,7 +2391,7 @@ dependencies = [ [[package]] name = "move-regex-borrow-graph" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "itertools 0.10.5", "move-binary-format", @@ -2457,7 +2434,7 @@ dependencies = [ [[package]] name = "move-stdlib" version = "0.1.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "anyhow", "hex", @@ -2476,7 +2453,7 @@ dependencies = [ [[package]] name = "move-stdlib-natives" version = "0.1.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "hex", "move-binary-format", @@ -2491,7 +2468,7 @@ dependencies = [ [[package]] name = "move-symbol-pool" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "once_cell", "phf", @@ -2501,7 +2478,7 @@ dependencies = [ [[package]] name = "move-trace-format" version = "0.0.1" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "move-binary-format", "move-core-types", @@ -2513,7 +2490,7 @@ dependencies = [ [[package]] name = "move-vm-config" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "move-binary-format", "once_cell", @@ -2522,7 +2499,7 @@ dependencies = [ [[package]] name = "move-vm-profiler" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "move-trace-format", "move-vm-config", @@ -2535,7 +2512,7 @@ dependencies = [ [[package]] name = "move-vm-runtime" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "better_any", "fail", @@ -2554,7 +2531,7 @@ dependencies = [ [[package]] name = "move-vm-types" version = "0.1.0" -source = "git+https://github.com/asymptotic-code/sui?branch=next#eb9c7c6459f69f9c968f19a21ec2897adfdfa7ab" +source = "git+https://github.com/asymptotic-code/sui?branch=next#0d772e55053ef62a37ae390c366ced0ba2ba056b" dependencies = [ "bcs", "move-binary-format", @@ -2580,17 +2557,17 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.18" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -2601,7 +2578,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -2613,7 +2590,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -2764,7 +2741,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -2781,9 +2758,15 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + [[package]] name = "openssl-probe" version = "0.2.1" @@ -2906,7 +2889,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3028,9 +3011,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.6" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", "ucd-trie", @@ -3038,9 +3021,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.6" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" dependencies = [ "pest", "pest_generator", @@ -3048,22 +3031,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.6" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "pest_meta" -version = "2.8.6" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" dependencies = [ "pest", "sha2 0.10.9", @@ -3121,7 +3104,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3150,7 +3133,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3215,16 +3198,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - [[package]] name = "primitive-types" version = "0.10.1" @@ -3297,13 +3270,13 @@ dependencies = [ [[package]] name = "proptest" -version = "1.10.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.11.0", + "bitflags 2.10.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -3322,9 +3295,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psm" -version = "0.1.30" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +checksum = "1fa96cb91275ed31d6da3e983447320c4eb219ac180fa1679a0889ff32861e2d" dependencies = [ "ar_archive_writer", "cc", @@ -3541,16 +3514,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", ] [[package]] name = "redox_syscall" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", ] [[package]] @@ -3581,14 +3554,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -3598,9 +3571,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -3609,9 +3582,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" @@ -3675,11 +3648,11 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", @@ -3708,10 +3681,10 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", + "openssl-probe 0.2.1", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.5.1", ] [[package]] @@ -3764,9 +3737,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -3800,9 +3773,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -3818,32 +3791,39 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "3.7.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", + "bitflags 2.10.0", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] -name = "security-framework-sys" -version = "2.17.0" +name = "security-framework" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", + "security-framework-sys", ] [[package]] -name = "semver" -version = "1.0.27" +name = "security-framework-sys" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] [[package]] name = "serde" @@ -3896,7 +3876,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3931,7 +3911,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3967,7 +3947,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.2.1", + "schemars 1.2.0", "serde_core", "serde_json", "serde_with_macros", @@ -3983,7 +3963,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -4127,9 +4107,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.12" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slug" @@ -4175,9 +4155,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" dependencies = [ "cc", "cfg-if", @@ -4250,9 +4230,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -4279,7 +4259,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -4329,12 +4309,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.26.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", - "getrandom 0.4.1", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4407,7 +4387,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -4418,7 +4398,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -4454,9 +4434,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" dependencies = [ "deranged", "itoa", @@ -4475,9 +4455,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" dependencies = [ "num-conv", "time-core", @@ -4518,7 +4498,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -4687,7 +4667,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -4824,9 +4804,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" @@ -4840,12 +4820,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" @@ -4909,7 +4883,7 @@ checksum = "a1935e10c6f04d22688d07c0790f2fc0e1b1c5c2c55bc0cc87ed67656e587dd8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -4979,15 +4953,6 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasite" version = "0.1.0" @@ -4996,9 +4961,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.112" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d7d0fce354c88b7982aec4400b3e7fcf723c32737cef571bd165f7613557ee" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -5009,9 +4974,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.62" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee85afca410ac4abba5b584b12e77ea225db6ee5471d0aebaae0861166f9378a" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", "futures-util", @@ -5023,9 +4988,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.112" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55839b71ba921e4f75b674cb16f843f4b1f3b26ddfcb3454de1cf65cc021ec0f" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5033,65 +4998,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.112" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf2e969c2d60ff52e7e98b7392ff1588bffdd1ccd4769eba27222fd3d621571" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.112" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0861f0dcdf46ea819407495634953cdcc8a8c7215ab799a7a7ce366be71c7b30" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.13.0", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap 2.13.0", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.89" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10053fbf9a374174094915bbce141e87a6bf32ecd9a002980db4b638405e8962" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -5103,14 +5034,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.5", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" dependencies = [ "rustls-pki-types", ] @@ -5184,7 +5115,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -5195,7 +5126,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -5486,88 +5417,6 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap 2.13.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap 2.13.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.13.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] [[package]] name = "writeable" @@ -5618,28 +5467,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "dafd85c832c1b68bbb4ec0c72c7f6f4fc5179627d2bc7c26b30e4c0cc11e76cc" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "7cb7e4e8436d9db52fbd6625dbf2f45243ab84994a72882ec8227b99e72b439a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -5659,7 +5508,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", "synstructure", ] @@ -5699,14 +5548,14 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" [[package]] name = "zstd" diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs index 97ef8e662e..b50fb2036c 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs @@ -103,7 +103,6 @@ struct DynamicFieldInfo { fun_borrow: String, fun_borrow_mut: String, fun_remove: String, - fun_remove_if_exists: String, fun_exists_with_type: String, fun_exists: String, fun_exists_inner: String, @@ -519,32 +518,6 @@ pub fn add_prelude( Ok(()) } -fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { - triple_opt - .and_then(|fun_qid| { - let module_env = env.get_module(fun_qid.module_id); - if module_env - .into_functions() - .any(|f| f.get_id() == fun_qid.id) - { - let fun = env.get_function(fun_qid); - Some(format!( - "${}_{}_{}", - fun.module_env.get_name().addr().to_str_radix(16), - fun.module_env.get_name().name().display(fun.symbol_pool()), - fun.get_name_str(), - )) - } else { - println!( - "Warning: function with id {:?} not found in module {:?}", - fun_qid.id, fun_qid.module_id - ); - None - } - }) - .unwrap_or_default() -} - impl QuantifierHelperInfo { fn new(env: &GlobalEnv, info: &PureQuantifierHelperInfo) -> Self { let func_env = env.get_function(info.function); @@ -670,19 +643,19 @@ impl TableImpl { }) .unwrap_or_default() { - triple_opt_to_name(env, env.table_new_qid()) + Self::triple_opt_to_name(env, env.table_new_qid()) } else { "".to_string() }, - fun_add: triple_opt_to_name(env, env.table_add_qid()), - fun_borrow: triple_opt_to_name(env, env.table_borrow_qid()), - fun_borrow_mut: triple_opt_to_name(env, env.table_borrow_mut_qid()), - fun_remove: triple_opt_to_name(env, env.table_remove_qid()), - fun_contains: triple_opt_to_name(env, env.table_contains_qid()), - fun_length: triple_opt_to_name(env, env.table_length_qid()), - fun_is_empty: triple_opt_to_name(env, env.table_is_empty_qid()), - fun_destroy_empty: triple_opt_to_name(env, env.table_destroy_empty_qid()), - fun_drop: triple_opt_to_name(env, env.table_drop_qid()), + fun_add: Self::triple_opt_to_name(env, env.table_add_qid()), + fun_borrow: Self::triple_opt_to_name(env, env.table_borrow_qid()), + fun_borrow_mut: Self::triple_opt_to_name(env, env.table_borrow_mut_qid()), + fun_remove: Self::triple_opt_to_name(env, env.table_remove_qid()), + fun_contains: Self::triple_opt_to_name(env, env.table_contains_qid()), + fun_length: Self::triple_opt_to_name(env, env.table_length_qid()), + fun_is_empty: Self::triple_opt_to_name(env, env.table_is_empty_qid()), + fun_destroy_empty: Self::triple_opt_to_name(env, env.table_destroy_empty_qid()), + fun_drop: Self::triple_opt_to_name(env, env.table_drop_qid()), fun_value_id: "".to_string(), } } @@ -727,22 +700,36 @@ impl TableImpl { }) .unwrap_or_default() { - triple_opt_to_name(env, env.object_table_new_qid()) + Self::triple_opt_to_name(env, env.object_table_new_qid()) } else { "".to_string() }, - fun_add: triple_opt_to_name(env, env.object_table_add_qid()), - fun_borrow: triple_opt_to_name(env, env.object_table_borrow_qid()), - fun_borrow_mut: triple_opt_to_name(env, env.object_table_borrow_mut_qid()), - fun_remove: triple_opt_to_name(env, env.object_table_remove_qid()), - fun_contains: triple_opt_to_name(env, env.object_table_contains_qid()), - fun_length: triple_opt_to_name(env, env.object_table_length_qid()), - fun_is_empty: triple_opt_to_name(env, env.object_table_is_empty_qid()), - fun_destroy_empty: triple_opt_to_name(env, env.object_table_destroy_empty_qid()), + fun_add: Self::triple_opt_to_name(env, env.object_table_add_qid()), + fun_borrow: Self::triple_opt_to_name(env, env.object_table_borrow_qid()), + fun_borrow_mut: Self::triple_opt_to_name(env, env.object_table_borrow_mut_qid()), + fun_remove: Self::triple_opt_to_name(env, env.object_table_remove_qid()), + fun_contains: Self::triple_opt_to_name(env, env.object_table_contains_qid()), + fun_length: Self::triple_opt_to_name(env, env.object_table_length_qid()), + fun_is_empty: Self::triple_opt_to_name(env, env.object_table_is_empty_qid()), + fun_destroy_empty: Self::triple_opt_to_name(env, env.object_table_destroy_empty_qid()), fun_drop: "".to_string(), - fun_value_id: triple_opt_to_name(env, env.object_table_value_id_qid()), + fun_value_id: Self::triple_opt_to_name(env, env.object_table_value_id_qid()), } } + + fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { + triple_opt + .map(|fun_qid| { + let fun = env.get_function(fun_qid); + format!( + "${}_{}_{}", + fun.module_env.get_name().addr().to_str_radix(16), + fun.module_env.get_name().name().display(fun.symbol_pool()), + fun.get_name_str(), + ) + }) + .unwrap_or_default() + } } impl DynamicFieldInfo { @@ -775,13 +762,15 @@ impl DynamicFieldInfo { struct_name: boogie_type_suffix_bv(env, tp, bv_flag), insts, key_insts, - fun_add: triple_opt_to_name(env, env.dynamic_field_add_qid()), - fun_borrow: triple_opt_to_name(env, env.dynamic_field_borrow_qid()), - fun_borrow_mut: triple_opt_to_name(env, env.dynamic_field_borrow_mut_qid()), - fun_remove: triple_opt_to_name(env, env.dynamic_field_remove_qid()), - fun_remove_if_exists: triple_opt_to_name(env, env.dynamic_field_remove_if_exists_qid()), - fun_exists_with_type: triple_opt_to_name(env, env.dynamic_field_exists_with_type_qid()), - fun_exists: triple_opt_to_name(env, env.dynamic_field_exists_qid()), + fun_add: Self::triple_opt_to_name(env, env.dynamic_field_add_qid()), + fun_borrow: Self::triple_opt_to_name(env, env.dynamic_field_borrow_qid()), + fun_borrow_mut: Self::triple_opt_to_name(env, env.dynamic_field_borrow_mut_qid()), + fun_remove: Self::triple_opt_to_name(env, env.dynamic_field_remove_qid()), + fun_exists_with_type: Self::triple_opt_to_name( + env, + env.dynamic_field_exists_with_type_qid(), + ), + fun_exists: Self::triple_opt_to_name(env, env.dynamic_field_exists_qid()), fun_exists_inner: env .dynamic_field_exists_qid() .map(|fun_qid| { @@ -825,19 +814,18 @@ impl DynamicFieldInfo { struct_name: boogie_type_suffix_bv(env, tp, bv_flag), insts, key_insts, - fun_add: triple_opt_to_name(env, env.dynamic_object_field_add_qid()), - fun_borrow: triple_opt_to_name(env, env.dynamic_object_field_borrow_qid()), - fun_borrow_mut: triple_opt_to_name(env, env.dynamic_object_field_borrow_mut_qid()), - fun_remove: triple_opt_to_name(env, env.dynamic_object_field_remove_qid()), - fun_remove_if_exists: triple_opt_to_name( + fun_add: Self::triple_opt_to_name(env, env.dynamic_object_field_add_qid()), + fun_borrow: Self::triple_opt_to_name(env, env.dynamic_object_field_borrow_qid()), + fun_borrow_mut: Self::triple_opt_to_name( env, - env.dynamic_object_field_remove_if_exists_qid(), + env.dynamic_object_field_borrow_mut_qid(), ), - fun_exists_with_type: triple_opt_to_name( + fun_remove: Self::triple_opt_to_name(env, env.dynamic_object_field_remove_qid()), + fun_exists_with_type: Self::triple_opt_to_name( env, env.dynamic_object_field_exists_with_type_qid(), ), - fun_exists: triple_opt_to_name(env, env.dynamic_object_field_exists_qid()), + fun_exists: Self::triple_opt_to_name(env, env.dynamic_object_field_exists_qid()), fun_exists_inner: env .dynamic_object_field_exists_qid() .map(|fun_qid| { @@ -851,4 +839,18 @@ impl DynamicFieldInfo { .unwrap_or_default(), } } + + fn triple_opt_to_name(env: &GlobalEnv, triple_opt: Option>) -> String { + triple_opt + .map(|fun_qid| { + let fun = env.get_function(fun_qid); + format!( + "${}_{}_{}", + fun.module_env.get_name().addr().to_str_radix(16), + fun.module_env.get_name().name().display(fun.symbol_pool()), + fun.get_name_str(), + ) + }) + .unwrap_or_default() + } } diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl b/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl index 10c8d33af2..7f26c301f3 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl +++ b/crates/move-prover-boogie-backend/src/boogie_backend/prelude/native.bpl @@ -927,26 +927,6 @@ procedure {:inline 2} {{impl.fun_remove}}{{DF_S}}(m: $Mutation ({{Type}}), k: {{ } {%- endif %} -{%- if impl.fun_remove_if_exists != "" %} -// remove_if_exists: removes the dynamic field if it exists, otherwise no-op -procedure {:inline 2} {{impl.fun_remove_if_exists}}{{DF_S}}(m: $Mutation ({{Type}}), k: {{K}}) returns (v: $1_option_Option'{{instance.1.suffix}}', m': $Mutation({{Type}})) { - var enc_k: int; - var t: {{Type}}; - var val: {{V}}; - enc_k := {{ENC}}(k); - t := $Dereference(m); - if (ContainsTable(t->$dynamic_fields{{S}}, enc_k)) { - val := GetTable(t->$dynamic_fields{{S}}, enc_k); - assume $IsValid{{SV}}(val); - m' := $UpdateMutation(m, $Update'{{Type}}'_dynamic_fields{{S}}(t, RemoveTable(t->$dynamic_fields{{S}}, enc_k))); - v := $1_option_Option{{SV}}(MakeVec1(val)); - } else { - m' := m; - v := $1_option_Option{{SV}}(EmptyVec()); - } -} -{%- endif %} - {%- if impl.fun_exists_with_type != "" %} function {:inline} {{impl.fun_exists_with_type}}{{DF_S}}(t: ({{Type}}), k: {{K}}): bool { ContainsTable(t->$dynamic_fields{{S}}, {{ENC}}(k)) diff --git a/crates/move-stackless-bytecode/src/mono_analysis.rs b/crates/move-stackless-bytecode/src/mono_analysis.rs index d32a44cbe5..9263d67a13 100644 --- a/crates/move-stackless-bytecode/src/mono_analysis.rs +++ b/crates/move-stackless-bytecode/src/mono_analysis.rs @@ -72,8 +72,16 @@ impl MonoInfo { } if dt_qid == &env.option_qid().unwrap() { - // NOTE: cover all option usages is too complex, so we just return true. - return true; + // NOTE: We disable this optimization to make extra bpl more flexible. + return targets.has_targeted_extra_bpl(env) + || self.is_used_datatype_helper(env, targets, dt_qid) + || self.is_used_datatype_helper(env, targets, &env.vec_set_qid().unwrap()) + || self.is_used_datatype_helper(env, targets, &env.vec_map_qid().unwrap()) + || self.is_generated_module( + env, + targets, + &vec![env.vec_set_module_id(), env.vec_map_module_id()], + ); } else if dt_qid == &env.vec_map_entry_qid().unwrap() || dt_qid == &env.vec_map_qid().unwrap() { diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move deleted file mode 100644 index 2146905ed2..0000000000 --- a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_remove.ok.move +++ /dev/null @@ -1,20 +0,0 @@ -module 0x42::foo; - -use sui::dynamic_field as df; - -public struct WhitelistKey has copy, store, drop { - address: address, -} - -public struct MyObject has key { - id: UID, -} - -public fun remove_whitelist_address(uid: &mut UID, addr: address) { - df::remove_if_exists(uid, WhitelistKey { address: addr }); -} - -#[spec(prove)] -fun remove_whitelist_spec(obj: &mut MyObject, addr: address) { - remove_whitelist_address(&mut obj.id, addr); -} diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap deleted file mode 100644 index c4de20f8bf..0000000000 --- a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_remove.ok.move.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/sui-prover/tests/integration.rs -assertion_line: 297 -expression: output ---- -Verification successful From 2450cbfda416107e68a8c144230913fc177b2533 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Thu, 12 Mar 2026 22:14:09 +0000 Subject: [PATCH 05/21] fix: use UID as fallback object type when parent is unknown in dynamic field analysis When a function takes &UID directly (rather than extracting from an object), the analysis can't determine the parent object type. Instead of emitting an error (E0022), fall back to placing dynamic fields under UID itself with a warning. If any dynamic field for a UID needs fallback, all fields for that UID use UID type for consistency. Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 436 +++++++----------- .../dynamic_field/issue-237.fail.move.snap | 9 +- 2 files changed, 175 insertions(+), 270 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index bf7d92d1de..5bbd70a354 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -14,7 +14,7 @@ use crate::{ use codespan_reporting::diagnostic::{Diagnostic, Label, Severity}; use itertools::Itertools; use move_model::{ - model::{DatatypeId, FunctionEnv, GlobalEnv, QualifiedId, StructEnv}, + model::{FunctionEnv, GlobalEnv, StructEnv}, ty::Type, }; use std::{ @@ -26,7 +26,6 @@ use std::{ pub struct DynamicFieldInfo { dynamic_field_mappings: BTreeMap>, uid_info: BTreeMap, - uid_param_object_types: BTreeMap>, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -57,6 +56,28 @@ impl NameValueInfo { NameValueInfo::NameOnly(name) => name, } } + + pub fn is_open(&self) -> bool { + match self { + NameValueInfo::NameValue { name, value, .. } => name.is_open() || value.is_open(), + NameValueInfo::NameOnly(name) => name.is_open(), + } + } + + pub fn instantiate(&self, type_inst: &[Type]) -> Self { + match self { + NameValueInfo::NameValue { + name, + value, + is_mut, + } => NameValueInfo::NameValue { + name: name.instantiate(type_inst), + value: value.instantiate(type_inst), + is_mut: *is_mut, + }, + NameValueInfo::NameOnly(name) => NameValueInfo::NameOnly(name.instantiate(type_inst)), + } + } } impl DynamicFieldInfo { @@ -64,7 +85,6 @@ impl DynamicFieldInfo { Self { dynamic_field_mappings: BTreeMap::new(), uid_info: BTreeMap::new(), - uid_param_object_types: BTreeMap::new(), } } @@ -72,7 +92,6 @@ impl DynamicFieldInfo { Self { dynamic_field_mappings: BTreeMap::from([(ty, BTreeSet::from([name_value_info]))]), uid_info: BTreeMap::new(), - uid_param_object_types: BTreeMap::new(), } } @@ -101,20 +120,12 @@ impl DynamicFieldInfo { .or_insert_with(BTreeSet::new) .extend(name_value_set.iter().cloned()); } - for (param_idx, obj_types) in other.uid_param_object_types.iter() { - new_info - .uid_param_object_types - .entry(*param_idx) - .or_insert_with(BTreeSet::new) - .extend(obj_types.iter().cloned()); - } new_info } pub fn instantiate(&self, type_inst: &[Type]) -> Self { Self { uid_info: BTreeMap::new(), - uid_param_object_types: BTreeMap::new(), dynamic_field_mappings: self .dynamic_field_mappings .iter() @@ -123,20 +134,7 @@ impl DynamicFieldInfo { ty.instantiate(type_inst), name_value_set .iter() - .map(|name_value_info| match name_value_info { - NameValueInfo::NameValue { - name, - value, - is_mut, - } => NameValueInfo::NameValue { - name: name.instantiate(type_inst), - value: value.instantiate(type_inst), - is_mut: *is_mut, - }, - NameValueInfo::NameOnly(name) => { - NameValueInfo::NameOnly(name.instantiate(type_inst)) - } - }) + .map(|nv| nv.instantiate(type_inst)) .collect(), ) }) @@ -270,91 +268,70 @@ fn collect_dynamic_field_info( Bytecode::Call(_, _, Operation::Function(module_id, fun_id, type_inst), srcs, _) => { let callee_id = module_id.qualified(*fun_id); - let uid_object_type_not_found_error = || { - if !verified_or_inlined - && !builder + if callee_id == builder.fun_env.get_qualified_id() { + return None; + } + + let uid_fallback_type = || -> Option { + let uid_qid = uid_qid?; + if verified_or_inlined + || builder .fun_env .get_return_types() .iter() .any(|x| x.is_mutable_reference()) { - return; + let loc = builder.get_loc(bc.get_attr_id()); + builder.fun_env.module_env.env.add_diag( + Diagnostic::new(Severity::Warning) + .with_message("UID object type not found, dynamic fields will be placed under UID") + .with_labels(vec![Label::primary(loc.file_id(), loc.span())]), + ); } - - let loc = builder.get_loc(bc.get_attr_id()); - builder.fun_env.module_env.env.add_diag( - Diagnostic::new(Severity::Error) - .with_code("E0022") - .with_message(&format!("UID object type not found: {}", srcs[0])) - .with_labels(vec![Label::primary(loc.file_id(), loc.span())]), - ); + Some(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])) }; - if callee_id == builder.fun_env.get_qualified_id() { - return None; - } - - let has_type_params = !builder.fun_env.get_type_parameters().is_empty(); - if dynamic_field_name_value_fun_qids.contains(&callee_id) { - if let Some((_, obj_type)) = get_uid_object_type_impl( - &uid_info, - &alias_info, - srcs[0], - offset, - has_type_params, - uid_qid, - ) { + let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) + .map(|(_, t)| t.clone()) + .or_else(&uid_fallback_type); + if let Some(obj_type) = obj_type { return Some(DynamicFieldInfo::singleton( - obj_type.clone(), + obj_type, NameValueInfo::NameValue { name: type_inst[0].clone(), value: type_inst[1].clone(), is_mut: false, }, )); - } else { - uid_object_type_not_found_error(); } } if dynamic_field_name_value_fun_mut_qids.contains(&callee_id) { - if let Some((_, obj_type)) = get_uid_object_type_impl( - &uid_info, - &alias_info, - srcs[0], - offset, - has_type_params, - uid_qid, - ) { + let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) + .map(|(_, t)| t.clone()) + .or_else(&uid_fallback_type); + if let Some(obj_type) = obj_type { return Some(DynamicFieldInfo::singleton( - obj_type.clone(), + obj_type, NameValueInfo::NameValue { name: type_inst[0].clone(), value: type_inst[1].clone(), is_mut: true, }, )); - } else { - uid_object_type_not_found_error(); } } if dynamic_field_name_only_fun_qids.contains(&callee_id) { - if let Some((_, obj_type)) = get_uid_object_type_impl( - &uid_info, - &alias_info, - srcs[0], - offset, - has_type_params, - uid_qid, - ) { + let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) + .map(|(_, t)| t.clone()) + .or_else(&uid_fallback_type); + if let Some(obj_type) = obj_type { return Some(DynamicFieldInfo::singleton( - obj_type.clone(), + obj_type, NameValueInfo::NameOnly(type_inst[0].clone()), )); - } else { - uid_object_type_not_found_error(); } } @@ -381,10 +358,7 @@ fn collect_dynamic_field_info( )); let callee_info = get_fun_info(callee_data); - let substituted_info = - substitute_uid_params(callee_info, &uid_info, &alias_info, srcs, offset); - - Some(substituted_info.instantiate(type_inst)) + Some(callee_info.instantiate(type_inst)) } _ => None, }, @@ -453,6 +427,12 @@ fn collect_dynamic_field_info( } type_inst.push(obj_type.clone()); } + } else if !type_inst.iter().any(|t| t.is_open()) { + // Fallback: push UID type only for concrete df calls where parent + // is unknown. Generic calls get concrete types via monomorphization. + if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { + type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); + } } builder.emit(Bytecode::Call( attr_id, @@ -469,35 +449,6 @@ fn collect_dynamic_field_info( info } -fn substitute_uid_params( - callee_info: &DynamicFieldInfo, - caller_uid_info: &BTreeMap, - alias_info: &BTreeMap, - srcs: &[usize], - offset: usize, -) -> DynamicFieldInfo { - let mut result = callee_info.clone(); - - for (temp_idx, (obj_local, uid_type)) in &callee_info.uid_info { - if temp_idx == obj_local && *temp_idx < srcs.len() { - let src_idx = srcs[*temp_idx]; - if let Some((_, actual_obj_type)) = - get_uid_object_type(caller_uid_info, alias_info, src_idx, offset) - { - if let Some(uid_name_values) = callee_info.dynamic_field_mappings.get(uid_type) { - result - .dynamic_field_mappings - .entry(actual_obj_type.clone()) - .or_insert_with(BTreeSet::new) - .extend(uid_name_values.iter().cloned()); - } - } - } - } - - result -} - fn compute_uid_info( fun_target: &FunctionTarget, targets: &FunctionTargetsHolder, @@ -673,45 +624,15 @@ fn get_uid_object_type<'a>( temp_idx: usize, off: usize, ) -> Option<&'a (usize, Type)> { - get_uid_object_type_impl(uid_info, alias_info, temp_idx, off, false, None) -} - -fn get_uid_object_type_impl<'a>( - uid_info: &'a BTreeMap, - alias_info: &'a BTreeMap, - temp_idx: usize, - off: usize, - skip_uid_params: bool, - uid_qid: Option>, -) -> Option<&'a (usize, Type)> { - let should_skip = |obj_type: &Type| -> bool { - if !skip_uid_params { - return false; - } - if let Some(uid_qid) = uid_qid { - if let Some((qid, _)) = obj_type.get_datatype() { - return qid == uid_qid; - } - } - false - }; - - uid_info - .get(&temp_idx) - .filter(|(_, obj_type)| !should_skip(obj_type)) - .or_else(|| { - alias_info.get(&(off as u16)).and_then(|state| { - ReachingDefProcessor::all_aliases(state, &temp_idx) - .iter() - .filter_map(|alias| { - uid_info - .get(alias) - .filter(|(_, obj_type)| !should_skip(obj_type)) - }) - .exactly_one() - .ok() - }) + uid_info.get(&temp_idx).or_else(|| { + alias_info.get(&(off as u16)).and_then(|state| { + ReachingDefProcessor::all_aliases(state, &temp_idx) + .iter() + .filter_map(|alias| uid_info.get(alias)) + .exactly_one() + .ok() }) + }) } pub struct DynamicFieldAnalysisProcessor(); @@ -754,136 +675,125 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } fn finalize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { - let uid_param_types = collect_uid_param_info(env, targets); - - for (callee_id, param_types) in &uid_param_types { - if let Some(data) = targets.get_data_mut(callee_id, &FunctionVariant::Baseline) { - if let Some(callee_info) = data.annotations.get::().cloned() { - let mut updated_info = callee_info.clone(); - - for (param_idx, obj_types) in param_types { - if let Some((obj_local, uid_type)) = callee_info.uid_info.get(param_idx) { - if obj_local == param_idx { - if let Some(name_values) = - callee_info.dynamic_field_mappings.get(uid_type) - { - for obj_type in obj_types { - updated_info - .dynamic_field_mappings - .entry(obj_type.clone()) - .or_insert_with(BTreeSet::new) - .extend(name_values.iter().cloned()); - - updated_info - .uid_param_object_types - .entry(*param_idx) - .or_insert_with(BTreeSet::new) - .insert(obj_type.clone()); - } - } + let mut combined_info = + DynamicFieldInfo::iter_union(targets.get_funs().filter_map(|fun_id| { + targets + .get_data(&fun_id, &FunctionVariant::Baseline) + .and_then(|data| { + data.annotations + .get::() + .map(|info| info.clone()) + }) + })); + + // Propagate UID-keyed entries through the call graph. + // Generic functions with UID params (e.g. borrow_mut(&mut UID, ...)) + // have UID → {u64, T} entries with open types. Callee info propagation + // is skipped for reachable functions, so concrete instantiations from + // callers (e.g. borrow_mut) don't appear in annotations. + // This pass transitively instantiates UID entries through the call graph + // to collect concrete entries like UID → {u64, Validator}. + let uid_qid = env.uid_qid(); + if let Some(uid_qid) = uid_qid { + let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); + + // Collect initial UID entries per function + let funs: Vec<_> = targets.get_funs().collect(); + let mut uid_entries: BTreeMap<_, BTreeSet> = BTreeMap::new(); + for &fun_id in &funs { + if let Some(data) = targets.get_data(&fun_id, &FunctionVariant::Baseline) { + if let Some(info) = data.annotations.get::() { + if let Some(entries) = info.dynamic_field_mappings.get(&uid_type) { + if !entries.is_empty() { + uid_entries.insert(fun_id, entries.clone()); } } } - - data.annotations.set(updated_info, true); } } - } - - let combined_info = DynamicFieldInfo::iter_union(targets.get_funs().filter_map(|fun_id| { - targets - .get_data(&fun_id, &FunctionVariant::Baseline) - .and_then(|data| { - data.annotations - .get::() - .map(|info| info.clone()) - }) - })); - - env.set_extension(combined_info); - } - - fn name(&self) -> String { - "dynamic_field_analysis_processor".to_string() - } -} - -fn collect_uid_param_info( - env: &GlobalEnv, - targets: &FunctionTargetsHolder, -) -> BTreeMap, BTreeMap>> { - let mut param_types: BTreeMap< - QualifiedId, - BTreeMap>, - > = BTreeMap::new(); - - for fun_id in targets.get_funs() { - if let Some(data) = targets.get_data(&fun_id, &FunctionVariant::Baseline) { - let fun_env = env.get_function(fun_id); - if fun_env.is_native() || fun_env.is_intrinsic() { - continue; - } - - let caller_info = match data.annotations.get::() { - Some(info) => info, - None => continue, - }; - let alias_info = ReachingDefProcessor::analyze_reaching_definitions(&fun_env, data); - - for (offset, bc) in data.code.iter().enumerate() { - if let Bytecode::Call( - _, - _, - Operation::Function(module_id, callee_fun_id, _type_inst), - srcs, - _, - ) = bc - { - let callee_id = module_id.qualified(*callee_fun_id); - - if callee_id == fun_id { - continue; - } - - let callee_data = match targets.get_data(&callee_id, &FunctionVariant::Baseline) - { - Some(d) => d, - None => continue, - }; - let callee_info = match callee_data.annotations.get::() { - Some(info) => info, - None => continue, - }; - - for (param_idx, (obj_local, _uid_type)) in &callee_info.uid_info { - if param_idx == obj_local && *param_idx < srcs.len() { - let src_idx = srcs[*param_idx]; - if let Some((_, actual_obj_type)) = get_uid_object_type( - &caller_info.uid_info, - &alias_info, - src_idx, - offset, - ) { - if let Some((local, _)) = caller_info.uid_info.get(&src_idx) { - if *local == src_idx { + // Propagate: if a callee has UID entries, the caller inherits them + // (instantiated with the call's type args). Repeat until fixpoint. + if !uid_entries.is_empty() { + loop { + let mut changed = false; + for &fun_id in &funs { + if let Some(data) = targets.get_data(&fun_id, &FunctionVariant::Baseline) { + let mut new_entries = BTreeSet::new(); + for bc in &data.code { + if let Bytecode::Call( + _, + _, + Operation::Function(mid, fid, type_inst), + _, + _, + ) = bc + { + let callee_id = mid.qualified(*fid); + if callee_id == fun_id { continue; } + if let Some(callee_entries) = + uid_entries.get(&callee_id).cloned() + { + for nv in &callee_entries { + new_entries.insert(nv.instantiate(type_inst)); + } + } + } + } + if !new_entries.is_empty() { + let entry = uid_entries.entry(fun_id).or_insert_with(BTreeSet::new); + let old_len = entry.len(); + entry.extend(new_entries); + if entry.len() > old_len { + changed = true; } - - param_types - .entry(callee_id) - .or_insert_with(BTreeMap::new) - .entry(*param_idx) - .or_insert_with(BTreeSet::new) - .insert(actual_obj_type.clone()); } } } + if !changed { + break; + } + } + + // Add concrete UID entries to combined info + let concrete_entries: BTreeSet = uid_entries + .values() + .flat_map(|entries| entries.iter()) + .filter(|nv| !nv.is_open()) + .cloned() + .collect(); + if !concrete_entries.is_empty() { + combined_info + .dynamic_field_mappings + .entry(uid_type.clone()) + .or_insert_with(BTreeSet::new) + .extend(concrete_entries); } } + + // Remove open-typed UID entries. Generic functions create UID → {K, V} + // with unresolved type params that cause ill-founded Boogie types. + // Non-UID entries (e.g. Versioned → {u64, T}) are kept as the Boogie + // backend handles them correctly for generic spec functions. + combined_info + .dynamic_field_mappings + .retain(|ty, name_values| { + if let Some((qid, _)) = ty.get_datatype() { + if qid == uid_qid { + name_values.retain(|nv| !nv.is_open()); + return !name_values.is_empty(); + } + } + !ty.is_open() + }); } + + env.set_extension(combined_info); } - param_types + fn name(&self) -> String { + "dynamic_field_analysis_processor".to_string() + } } diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap index e2ea6d088f..c4de20f8bf 100644 --- a/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap +++ b/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap @@ -1,11 +1,6 @@ --- source: crates/sui-prover/tests/integration.rs -assertion_line: 260 +assertion_line: 297 expression: output --- -exiting with bytecode transformation errors -error[E0022]: UID object type not found: 2 - ┌─ tests/inputs/dynamic_field/issue-237.fail.move:19:5 - │ -19 │ dynamic_field::borrow_mut(id, version) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Verification successful From a47c962ec170621518e5c46b4e1f31e724cc45d1 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Thu, 12 Mar 2026 22:20:17 +0000 Subject: [PATCH 06/21] restore comments removed in earlier commits Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 5bbd70a354..c81d8dd2c2 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -22,6 +22,7 @@ use std::{ rc::Rc, }; +/// Stores dynamic field type information for a specific function #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct DynamicFieldInfo { dynamic_field_mappings: BTreeMap>, @@ -88,6 +89,8 @@ impl DynamicFieldInfo { } } + /// Creates a DynamicFieldInfo with a single mapping from the given type to a single dynamic field type. + /// This is a convenience method for creating simple dynamic field entries. pub fn singleton(ty: Type, name_value_info: NameValueInfo) -> Self { Self { dynamic_field_mappings: BTreeMap::from([(ty, BTreeSet::from([name_value_info]))]), @@ -111,6 +114,7 @@ impl DynamicFieldInfo { .unique() } + /// union two DynamicFieldTypeInfo pub fn union(&self, other: &Self) -> Self { let mut new_info = self.clone(); for (ty, name_value_set) in other.dynamic_field_mappings.iter() { @@ -123,6 +127,7 @@ impl DynamicFieldInfo { new_info } + /// Create a new DynamicFieldTypeInfo with types instantiated using the given type arguments pub fn instantiate(&self, type_inst: &[Type]) -> Self { Self { uid_info: BTreeMap::new(), @@ -147,10 +152,12 @@ impl DynamicFieldInfo { } } +/// Get the information computed by this analysis for the global environment pub fn get_env_info(env: &GlobalEnv) -> Rc { env.get_extension::().unwrap() } +/// Get the information computed by this analysis for a function pub fn get_fun_info(data: &FunctionData) -> &DynamicFieldInfo { data.annotations.get::().unwrap() } @@ -159,6 +166,7 @@ pub fn get_function_return_local_pos(local_idx: usize, code: &[Bytecode]) -> Opt let mut return_pos = None; for bc in code.into_iter() { if return_pos.is_some() { + // if function have few return statments return None; } @@ -177,18 +185,21 @@ pub fn get_function_return_local_pos(local_idx: usize, code: &[Bytecode]) -> Opt return_pos } +/// Collect dynamic field type information from a function's bytecode fn collect_dynamic_field_info( targets: &FunctionTargetsHolder, builder: &mut FunctionDataBuilder, verified_or_inlined: bool, ) -> DynamicFieldInfo { let dynamic_field_name_value_fun_qids = vec![ + // dynamic field operations builder.fun_env.module_env.env.dynamic_field_borrow_qid(), builder .fun_env .module_env .env .dynamic_field_exists_with_type_qid(), + // dynamic object field operations builder .fun_env .module_env @@ -204,6 +215,7 @@ fn collect_dynamic_field_info( .filter_map(|x| x) .collect_vec(); let dynamic_field_name_value_fun_mut_qids = vec![ + // dynamic field operations builder.fun_env.module_env.env.dynamic_field_add_qid(), builder .fun_env @@ -216,6 +228,7 @@ fn collect_dynamic_field_info( .module_env .env .dynamic_field_remove_if_exists_qid(), + // dynamic object field operations builder .fun_env .module_env @@ -236,7 +249,9 @@ fn collect_dynamic_field_info( .filter_map(|x| x) .collect_vec(); let dynamic_field_name_only_fun_qids = vec![ + // dynamic field operations builder.fun_env.module_env.env.dynamic_field_exists_qid(), + // dynamic object field operations builder .fun_env .module_env @@ -258,6 +273,7 @@ fn collect_dynamic_field_info( let uid_qid = builder.fun_env.module_env.env.uid_qid(); + // compute map of temp index that load object ids to type of object let uid_info = compute_uid_info(&builder.get_target(), targets, &builder.data.code); let alias_info = @@ -345,6 +361,7 @@ fn collect_dynamic_field_info( .env .get_function(*fun_id_with_info); + // native, reachable or intrinsic functions do not access dynamic fields if func_env.is_native() || get_info(&builder.get_target()).reachable { return None; } @@ -449,6 +466,7 @@ fn collect_dynamic_field_info( info } +/// Computes a mapping from temporary indices to the objects and types of objects they reference fn compute_uid_info( fun_target: &FunctionTarget, targets: &FunctionTargetsHolder, @@ -476,6 +494,7 @@ fn compute_uid_info( BTreeMap::new() }; + // First collect all potential UID type mappings, grouped by temporary index let from_bytecode: BTreeMap = code .iter() .filter_map(|bc| match bc { @@ -544,6 +563,7 @@ fn compute_uid_info( .into_group_map() .into_iter() .filter_map(|(temp_idx, types)| { + // Report errors if there are duplicates if types.len() == 1 { Some((temp_idx, (types[0].0.clone(), types[0].1.clone()))) } else { @@ -590,6 +610,10 @@ fn compute_uid_info( result } +/// Checks if a field access at the given offset is accessing the single UID field. +/// Returns true if: +/// - The struct has the `key` ability and the offset is 0, OR +/// - The offset matches the single UID field offset fn is_uid_field_access(struct_env: &StructEnv<'_>, offset: usize) -> bool { (struct_env.get_abilities().has_key() && offset == 0) || Some(offset) == single_uid_field_offset(struct_env) @@ -624,7 +648,9 @@ fn get_uid_object_type<'a>( temp_idx: usize, off: usize, ) -> Option<&'a (usize, Type)> { + // First check if temp_idx is directly in uid_info uid_info.get(&temp_idx).or_else(|| { + // Otherwise, check if we can find it through alias information alias_info.get(&(off as u16)).and_then(|state| { ReachingDefProcessor::all_aliases(state, &temp_idx) .iter() @@ -664,6 +690,7 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { let mut builder = FunctionDataBuilder::new(fun_env, data); + // Collect the dynamic field info let info = collect_dynamic_field_info(targets, &mut builder, info.verified || info.inlined); builder @@ -675,6 +702,7 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } fn finalize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { + // Collect and combine all functions' dynamic field info let mut combined_info = DynamicFieldInfo::iter_union(targets.get_funs().filter_map(|fun_id| { targets @@ -790,6 +818,7 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { }); } + // Set the combined info in the environment env.set_extension(combined_info); } From a671275d4c03308403e027556046fcf2d5ea76e6 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 00:07:04 +0000 Subject: [PATCH 07/21] fix: consolidate dynamic field entries under UID when parent is unknown When a function mixes direct UID usage (from inlined callees taking &UID) with obj.id access, consolidate all dynamic field entries under UID type to ensure consistent Boogie slots. Simplifies compute_uid_info by removing UID parameter tracking and FreezeRef/ReadRef/Assign propagation. Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 206 +++++++----------- .../dynamic_field/issue-237.fail.move.snap | 5 + .../dynamic_field/uid_param.ok.move.snap | 5 + .../uid_param_exists.ok.move.snap | 5 + .../uid_param_target.ok.move.snap | 23 ++ 5 files changed, 116 insertions(+), 128 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index c81d8dd2c2..176f6a9341 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -271,8 +271,6 @@ fn collect_dynamic_field_info( .flatten() .collect_vec(); - let uid_qid = builder.fun_env.module_env.env.uid_qid(); - // compute map of temp index that load object ids to type of object let uid_info = compute_uid_info(&builder.get_target(), targets, &builder.data.code); @@ -284,12 +282,11 @@ fn collect_dynamic_field_info( Bytecode::Call(_, _, Operation::Function(module_id, fun_id, type_inst), srcs, _) => { let callee_id = module_id.qualified(*fun_id); - if callee_id == builder.fun_env.get_qualified_id() { - return None; - } - - let uid_fallback_type = || -> Option { - let uid_qid = uid_qid?; + // When the UID object type can't be determined (e.g. function takes + // &UID directly), fall back to placing dynamic fields under UID + // itself and emit a warning. + let uid_fallback = || -> Option { + let uid_qid = builder.fun_env.module_env.env.uid_qid()?; if verified_or_inlined || builder .fun_env @@ -307,10 +304,14 @@ fn collect_dynamic_field_info( Some(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])) }; + if callee_id == builder.fun_env.get_qualified_id() { + return None; + } + if dynamic_field_name_value_fun_qids.contains(&callee_id) { let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) .map(|(_, t)| t.clone()) - .or_else(&uid_fallback_type); + .or_else(&uid_fallback); if let Some(obj_type) = obj_type { return Some(DynamicFieldInfo::singleton( obj_type, @@ -326,7 +327,7 @@ fn collect_dynamic_field_info( if dynamic_field_name_value_fun_mut_qids.contains(&callee_id) { let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) .map(|(_, t)| t.clone()) - .or_else(&uid_fallback_type); + .or_else(&uid_fallback); if let Some(obj_type) = obj_type { return Some(DynamicFieldInfo::singleton( obj_type, @@ -342,7 +343,7 @@ fn collect_dynamic_field_info( if dynamic_field_name_only_fun_qids.contains(&callee_id) { let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) .map(|(_, t)| t.clone()) - .or_else(&uid_fallback_type); + .or_else(&uid_fallback); if let Some(obj_type) = obj_type { return Some(DynamicFieldInfo::singleton( obj_type, @@ -366,43 +367,47 @@ fn collect_dynamic_field_info( return None; } - let callee_data = targets + let info = targets .get_data(fun_id_with_info, &FunctionVariant::Baseline) + .map(|data| get_fun_info(data)) .expect(&format!( "callee `{}` of `{}` was filtered out", func_env.get_full_name_str(), builder.fun_env.get_full_name_str() )); - let callee_info = get_fun_info(callee_data); - - Some(callee_info.instantiate(type_inst)) + Some(info.instantiate(type_inst)) } _ => None, }, )); - info.uid_info = uid_info.clone(); - - let mut parents_with_uid_param_calls: BTreeSet = BTreeSet::new(); - for bc in builder.data.code.iter() { - if let Bytecode::Call(_, _, Operation::Function(module_id, callee_fun_id, _), srcs, _) = bc - { - let callee_id = module_id.qualified(*callee_fun_id); - if callee_id == builder.fun_env.get_qualified_id() { - continue; - } - if let Some(callee_data) = targets.get_data(&callee_id, &FunctionVariant::Baseline) { - let callee_info = get_fun_info(callee_data); - for (param_idx, (obj_local, _)) in &callee_info.uid_info { - if param_idx == obj_local && *param_idx < srcs.len() { - let uid_temp = srcs[*param_idx]; - if let Some((parent, _)) = uid_info.get(&uid_temp) { - parents_with_uid_param_calls.insert(*parent); - } - } - } - } - } + info.uid_info = uid_info; + + // If any dynamic field entries are keyed by UID type, consolidate ALL entries + // under UID. This ensures consistency: when a function mixes direct UID usage + // (e.g. from an inlined callee taking &UID) with obj.id access, all df operations + // use the same Boogie slot. Without this, verification fails because df operations + // on the same logical object would map to different Boogie variables. + let use_uid_type = builder + .fun_env + .module_env + .env + .uid_qid() + .map_or(false, |uid_qid| { + let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); + info.dynamic_field_mappings.contains_key(&uid_type) + }); + if use_uid_type { + let uid_qid = builder.fun_env.module_env.env.uid_qid().unwrap(); + let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); + // Merge all entries under UID type + let all_entries: BTreeSet = info + .dynamic_field_mappings + .values() + .flat_map(|s| s.iter().cloned()) + .collect(); + info.dynamic_field_mappings.clear(); + info.dynamic_field_mappings.insert(uid_type, all_entries); } let code = std::mem::take(&mut builder.data.code); @@ -415,35 +420,21 @@ fn collect_dynamic_field_info( mut srcs, aa, ) if all_dynamic_field_fun_qids.contains(&module_id.qualified(fun_id)) => { - if let Some((obj_local, obj_type)) = - get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) + if use_uid_type { + // All entries consolidated under UID — use UID type for all df calls + if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { + type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); + } + } else if let Some((obj_local, obj_type)) = + get_uid_object_type(&info.uid_info, &alias_info, srcs[0], offset) { - let use_uid_type = parents_with_uid_param_calls.contains(obj_local) - || parents_with_uid_param_calls.iter().any(|parent| { - alias_info.values().any(|state| { - let obj_local_defs = - ReachingDefProcessor::all_aliases(state, obj_local); - let parent_defs = ReachingDefProcessor::all_aliases(state, parent); - !obj_local_defs.is_disjoint(&parent_defs) - || obj_local_defs.contains(parent) - || parent_defs.contains(obj_local) - }) - }); - - if use_uid_type { - if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { - type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); - } - } else { - srcs[0] = *obj_local; - if !dynamic_field_name_value_fun_mut_qids - .contains(&module_id.qualified(fun_id)) - && builder.get_local_type(srcs[0]).is_mutable_reference() - { - srcs[0] = builder.emit_let_read_ref(srcs[0]); - } - type_inst.push(obj_type.clone()); + srcs[0] = *obj_local; + if !dynamic_field_name_value_fun_mut_qids.contains(&module_id.qualified(fun_id)) + && builder.get_local_type(srcs[0]).is_mutable_reference() + { + srcs[0] = builder.emit_let_read_ref(srcs[0]); } + type_inst.push(obj_type.clone()); } else if !type_inst.iter().any(|t| t.is_open()) { // Fallback: push UID type only for concrete df calls where parent // is unknown. Generic calls get concrete types via monomorphization. @@ -472,31 +463,8 @@ fn compute_uid_info( targets: &FunctionTargetsHolder, code: &[Bytecode], ) -> BTreeMap { - let env = fun_target.global_env(); - - let uid_params: BTreeMap = if let Some(uid_qid) = env.uid_qid() { - (0..fun_target.get_parameter_count()) - .filter_map(|idx| { - let ty = fun_target.get_local_type(idx); - let inner_ty = ty.skip_reference(); - if let Some((qid, tys)) = inner_ty.get_datatype() { - if qid == uid_qid { - return Some(( - idx, - (idx, Type::Datatype(qid.module_id, qid.id, tys.to_vec())), - )); - } - } - None - }) - .collect() - } else { - BTreeMap::new() - }; - - // First collect all potential UID type mappings, grouped by temporary index - let from_bytecode: BTreeMap = code - .iter() + code.iter() + // First collect all potential UID type mappings, grouped by temporary index .filter_map(|bc| match bc { Bytecode::Call( attr_id, @@ -583,31 +551,7 @@ fn compute_uid_info( None } }) - .collect(); - - let mut result = uid_params; - result.extend(from_bytecode); - - for bc in code { - match bc { - Bytecode::Call(_, dests, Operation::FreezeRef, srcs, _) - | Bytecode::Call(_, dests, Operation::ReadRef, srcs, _) => { - if !dests.is_empty() && !srcs.is_empty() { - if let Some((obj_local, obj_type)) = result.get(&srcs[0]).cloned() { - result.entry(dests[0]).or_insert((obj_local, obj_type)); - } - } - } - Bytecode::Assign(_, dest, src, _) => { - if let Some((obj_local, obj_type)) = result.get(src).cloned() { - result.entry(*dest).or_insert((obj_local, obj_type)); - } - } - _ => {} - } - } - - result + .collect() } /// Checks if a field access at the given offset is accessing the single UID field. @@ -703,29 +647,35 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { fn finalize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { // Collect and combine all functions' dynamic field info - let mut combined_info = - DynamicFieldInfo::iter_union(targets.get_funs().filter_map(|fun_id| { - targets - .get_data(&fun_id, &FunctionVariant::Baseline) - .and_then(|data| { - data.annotations - .get::() - .map(|info| info.clone()) - }) - })); + let mut combined_info = DynamicFieldInfo::iter_union( + targets + .specs() + .copied() + .chain(targets.get_funs().filter(|fun_id| { + targets.is_pure_fun(fun_id) || targets.is_abort_check_fun(fun_id) + })) + .filter_map(|fun_id| { + targets + .get_data(&fun_id, &FunctionVariant::Baseline) + .and_then(|data| { + data.annotations + .get::() + .map(|info| info.clone()) + }) + }), + ); // Propagate UID-keyed entries through the call graph. - // Generic functions with UID params (e.g. borrow_mut(&mut UID, ...)) + // Generic functions with UID fallback (e.g. borrow_mut(&mut UID, ...)) // have UID → {u64, T} entries with open types. Callee info propagation // is skipped for reachable functions, so concrete instantiations from // callers (e.g. borrow_mut) don't appear in annotations. // This pass transitively instantiates UID entries through the call graph // to collect concrete entries like UID → {u64, Validator}. - let uid_qid = env.uid_qid(); - if let Some(uid_qid) = uid_qid { + if let Some(uid_qid) = env.uid_qid() { let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); - // Collect initial UID entries per function + // Collect initial UID entries from ALL functions' annotations let funs: Vec<_> = targets.get_funs().collect(); let mut uid_entries: BTreeMap<_, BTreeSet> = BTreeMap::new(); for &fun_id in &funs { diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap index c4de20f8bf..aea30d236c 100644 --- a/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap +++ b/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap @@ -4,3 +4,8 @@ assertion_line: 297 expression: output --- Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/issue-237.fail.move:19:5 + │ +19 │ dynamic_field::borrow_mut(id, version) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap index c4de20f8bf..212afffbdc 100644 --- a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param.ok.move.snap @@ -4,3 +4,8 @@ assertion_line: 297 expression: output --- Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param.ok.move:15:5 + │ +15 │ df::add(uid, WhitelistKey { address: addr }, true); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap index c4de20f8bf..c18f8c7e05 100644 --- a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_exists.ok.move.snap @@ -4,3 +4,8 @@ assertion_line: 297 expression: output --- Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_exists.ok.move:14:5 + │ +14 │ df::exists_with_type(uid, WhitelistKey { address: addr }) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap index c4de20f8bf..0c484f478c 100644 --- a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_target.ok.move.snap @@ -4,3 +4,26 @@ assertion_line: 297 expression: output --- Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_target.ok.move:17:5 + │ +17 │ df::add(uid, WhitelistKey { address: addr }, true); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_target.ok.move:34:14 + │ +34 │ asserts(!df::exists_with_type(uid, whitelist_key(addr))); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_target.ok.move:21:5 + │ +21 │ df::add(uid, AllowAllKey {}, true); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_target.ok.move:40:14 + │ +40 │ asserts(!df::exists_with_type(uid, allow_all_key())); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 72ebf1a883e5e2600d0a3faf255ed738069f4517 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 01:18:57 +0000 Subject: [PATCH 08/21] fix: consolidate all dynamic field info under UID globally when warnings emitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When any function uses the UID fallback (warning emitted), finalize now consolidates all dynamic field entries under UID type — both the global combined_info and each function's per-function annotation. This ensures consistent Boogie slots across the entire program. Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 176f6a9341..86c838e434 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -690,9 +690,13 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } } - // Propagate: if a callee has UID entries, the caller inherits them - // (instantiated with the call's type args). Repeat until fixpoint. + // If any function has UID-keyed entries (i.e., warnings were emitted), + // consolidate all dynamic field info under UID — both the global + // combined_info and each function's per-function annotation. + // This ensures all Boogie slots are consistent across the entire program. if !uid_entries.is_empty() { + // Propagate: if a callee has UID entries, the caller inherits them + // (instantiated with the call's type args). Repeat until fixpoint. loop { let mut changed = false; for &fun_id in &funs { @@ -749,6 +753,40 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { .or_insert_with(BTreeSet::new) .extend(concrete_entries); } + + // Consolidate global combined_info: merge all entries under UID type + let all_entries: BTreeSet = combined_info + .dynamic_field_mappings + .values() + .flat_map(|s| s.iter().cloned()) + .collect(); + combined_info.dynamic_field_mappings.clear(); + combined_info + .dynamic_field_mappings + .insert(uid_type.clone(), all_entries); + + // Consolidate each function's per-function annotation under UID + for &fun_id in &funs { + if let Some(data) = targets.get_data_mut(&fun_id, &FunctionVariant::Baseline) { + if let Some(info) = data.annotations.get::() { + if !info.dynamic_field_mappings.is_empty() + && !info.dynamic_field_mappings.keys().all(|k| k == &uid_type) + { + let mut new_info = info.clone(); + let all: BTreeSet = new_info + .dynamic_field_mappings + .values() + .flat_map(|s| s.iter().cloned()) + .collect(); + new_info.dynamic_field_mappings.clear(); + new_info + .dynamic_field_mappings + .insert(uid_type.clone(), all); + data.annotations.set(new_info, true); + } + } + } + } } // Remove open-typed UID entries. Generic functions create UID → {K, V} From 0d185b57c207ae75702697e97d0d0610594e927a Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 01:41:34 +0000 Subject: [PATCH 09/21] refactor: move annotation consolidation to finalize, keep bytecode guard in process The annotation consolidation (merging all df entries under UID type) is handled entirely by finalize. Process only keeps the bytecode rewriting guard (use_uid_type) since type_inst must match the consolidated annotations and bytecodes can't be rewritten after emission. Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 86c838e434..a561080546 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -383,11 +383,8 @@ fn collect_dynamic_field_info( info.uid_info = uid_info; - // If any dynamic field entries are keyed by UID type, consolidate ALL entries - // under UID. This ensures consistency: when a function mixes direct UID usage - // (e.g. from an inlined callee taking &UID) with obj.id access, all df operations - // use the same Boogie slot. Without this, verification fails because df operations - // on the same logical object would map to different Boogie variables. + // Check if any entries use UID type — if so, all bytecode df calls must use + // UID type to match the consolidated annotations from finalize. let use_uid_type = builder .fun_env .module_env @@ -397,18 +394,6 @@ fn collect_dynamic_field_info( let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); info.dynamic_field_mappings.contains_key(&uid_type) }); - if use_uid_type { - let uid_qid = builder.fun_env.module_env.env.uid_qid().unwrap(); - let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); - // Merge all entries under UID type - let all_entries: BTreeSet = info - .dynamic_field_mappings - .values() - .flat_map(|s| s.iter().cloned()) - .collect(); - info.dynamic_field_mappings.clear(); - info.dynamic_field_mappings.insert(uid_type, all_entries); - } let code = std::mem::take(&mut builder.data.code); for (offset, bc) in code.into_iter().enumerate() { @@ -421,7 +406,7 @@ fn collect_dynamic_field_info( aa, ) if all_dynamic_field_fun_qids.contains(&module_id.qualified(fun_id)) => { if use_uid_type { - // All entries consolidated under UID — use UID type for all df calls + // All entries consolidated under UID by finalize — bytecode must match if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); } From cad045fb2363e9feab8950236ab82c2057883672 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 02:06:17 +0000 Subject: [PATCH 10/21] refactor: remove redundant UID fallback in bytecode rewriting The uid_fallback closure in info collection already handles the case where parent is unknown, and the use_uid_type guard ensures bytecode consistency. The extra else-if branch was redundant. Co-Authored-By: Claude Opus 4.6 --- .../move-stackless-bytecode/src/dynamic_field_analysis.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index a561080546..f54573237a 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -420,12 +420,6 @@ fn collect_dynamic_field_info( srcs[0] = builder.emit_let_read_ref(srcs[0]); } type_inst.push(obj_type.clone()); - } else if !type_inst.iter().any(|t| t.is_open()) { - // Fallback: push UID type only for concrete df calls where parent - // is unknown. Generic calls get concrete types via monomorphization. - if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { - type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); - } } builder.emit(Bytecode::Call( attr_id, From 4f025128159710bea396390eb2339404304af76a Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 02:35:29 +0000 Subject: [PATCH 11/21] refactor: detect UID usage in initialize, simplify process and finalize Move UID detection to initialize (runs before process). When any accessible function takes &UID and makes df calls, set a global flag. Process then routes all df operations through UID type directly. Finalize no longer needs to consolidate annotations or bytecodes. Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 332 +++++++++--------- 1 file changed, 170 insertions(+), 162 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index f54573237a..62177844fd 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -14,7 +14,7 @@ use crate::{ use codespan_reporting::diagnostic::{Diagnostic, Label, Severity}; use itertools::Itertools; use move_model::{ - model::{FunctionEnv, GlobalEnv, StructEnv}, + model::{FunctionEnv, GlobalEnv, QualifiedId, StructEnv}, ty::Type, }; use std::{ @@ -22,6 +22,10 @@ use std::{ rc::Rc, }; +/// When set as an env extension, signals that all dynamic field operations +/// should use UID type instead of the parent object type. +struct UseUidForDynamicFields; + /// Stores dynamic field type information for a specific function #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct DynamicFieldInfo { @@ -185,91 +189,74 @@ pub fn get_function_return_local_pos(local_idx: usize, code: &[Bytecode]) -> Opt return_pos } -/// Collect dynamic field type information from a function's bytecode -fn collect_dynamic_field_info( - targets: &FunctionTargetsHolder, - builder: &mut FunctionDataBuilder, - verified_or_inlined: bool, -) -> DynamicFieldInfo { - let dynamic_field_name_value_fun_qids = vec![ +/// Collect all dynamic field function QualifiedIds from the environment +fn collect_df_qids(env: &GlobalEnv) -> DfQids { + let name_value = vec![ // dynamic field operations - builder.fun_env.module_env.env.dynamic_field_borrow_qid(), - builder - .fun_env - .module_env - .env - .dynamic_field_exists_with_type_qid(), + env.dynamic_field_borrow_qid(), + env.dynamic_field_exists_with_type_qid(), // dynamic object field operations - builder - .fun_env - .module_env - .env - .dynamic_object_field_borrow_qid(), - builder - .fun_env - .module_env - .env - .dynamic_object_field_exists_with_type_qid(), + env.dynamic_object_field_borrow_qid(), + env.dynamic_object_field_exists_with_type_qid(), ] .into_iter() - .filter_map(|x| x) + .flatten() .collect_vec(); - let dynamic_field_name_value_fun_mut_qids = vec![ + let name_value_mut = vec![ // dynamic field operations - builder.fun_env.module_env.env.dynamic_field_add_qid(), - builder - .fun_env - .module_env - .env - .dynamic_field_borrow_mut_qid(), - builder.fun_env.module_env.env.dynamic_field_remove_qid(), - builder - .fun_env - .module_env - .env - .dynamic_field_remove_if_exists_qid(), + env.dynamic_field_add_qid(), + env.dynamic_field_borrow_mut_qid(), + env.dynamic_field_remove_qid(), + env.dynamic_field_remove_if_exists_qid(), // dynamic object field operations - builder - .fun_env - .module_env - .env - .dynamic_object_field_add_qid(), - builder - .fun_env - .module_env - .env - .dynamic_object_field_borrow_mut_qid(), - builder - .fun_env - .module_env - .env - .dynamic_object_field_remove_qid(), + env.dynamic_object_field_add_qid(), + env.dynamic_object_field_borrow_mut_qid(), + env.dynamic_object_field_remove_qid(), ] .into_iter() - .filter_map(|x| x) + .flatten() .collect_vec(); - let dynamic_field_name_only_fun_qids = vec![ + let name_only = vec![ // dynamic field operations - builder.fun_env.module_env.env.dynamic_field_exists_qid(), + env.dynamic_field_exists_qid(), // dynamic object field operations - builder - .fun_env - .module_env - .env - .dynamic_object_field_exists_qid(), - ] - .into_iter() - .filter_map(|x| x) - .collect_vec(); - let all_dynamic_field_fun_qids = [ - &dynamic_field_name_value_fun_qids, - &dynamic_field_name_value_fun_mut_qids, - &dynamic_field_name_only_fun_qids, + env.dynamic_object_field_exists_qid(), ] .into_iter() - .cloned() .flatten() .collect_vec(); + let all = [&name_value, &name_value_mut, &name_only] + .into_iter() + .cloned() + .flatten() + .collect_vec(); + DfQids { + name_value, + name_value_mut, + name_only, + all, + } +} + +struct DfQids { + name_value: Vec>, + name_value_mut: Vec>, + name_only: Vec>, + all: Vec>, +} + +/// Collect dynamic field type information from a function's bytecode +fn collect_dynamic_field_info( + targets: &FunctionTargetsHolder, + builder: &mut FunctionDataBuilder, + verified_or_inlined: bool, + use_uid: bool, +) -> DynamicFieldInfo { + let env = builder.fun_env.module_env.env; + let df_qids = collect_df_qids(env); + let uid_type = env + .uid_qid() + .map(|qid| Type::Datatype(qid.module_id, qid.id, vec![])); // compute map of temp index that load object ids to type of object let uid_info = compute_uid_info(&builder.get_target(), targets, &builder.data.code); @@ -282,11 +269,21 @@ fn collect_dynamic_field_info( Bytecode::Call(_, _, Operation::Function(module_id, fun_id, type_inst), srcs, _) => { let callee_id = module_id.qualified(*fun_id); - // When the UID object type can't be determined (e.g. function takes - // &UID directly), fall back to placing dynamic fields under UID - // itself and emit a warning. - let uid_fallback = || -> Option { - let uid_qid = builder.fun_env.module_env.env.uid_qid()?; + // Determine the object type for this df call. + // When use_uid is set (detected in initialize), always use UID type. + // Otherwise, look up the parent object type, falling back to UID + // with a warning when the parent can't be determined. + let get_obj_type = |offset: usize| -> Option { + if use_uid { + return uid_type.clone(); + } + if let Some((_, obj_type)) = + get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) + { + return Some(obj_type.clone()); + } + // UID fallback: emit warning when parent is unknown + let uid_qid = env.uid_qid()?; if verified_or_inlined || builder .fun_env @@ -295,9 +292,11 @@ fn collect_dynamic_field_info( .any(|x| x.is_mutable_reference()) { let loc = builder.get_loc(bc.get_attr_id()); - builder.fun_env.module_env.env.add_diag( + env.add_diag( Diagnostic::new(Severity::Warning) - .with_message("UID object type not found, dynamic fields will be placed under UID") + .with_message( + "UID object type not found, dynamic fields will be placed under UID", + ) .with_labels(vec![Label::primary(loc.file_id(), loc.span())]), ); } @@ -308,11 +307,8 @@ fn collect_dynamic_field_info( return None; } - if dynamic_field_name_value_fun_qids.contains(&callee_id) { - let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) - .map(|(_, t)| t.clone()) - .or_else(&uid_fallback); - if let Some(obj_type) = obj_type { + if df_qids.name_value.contains(&callee_id) { + if let Some(obj_type) = get_obj_type(offset) { return Some(DynamicFieldInfo::singleton( obj_type, NameValueInfo::NameValue { @@ -324,11 +320,8 @@ fn collect_dynamic_field_info( } } - if dynamic_field_name_value_fun_mut_qids.contains(&callee_id) { - let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) - .map(|(_, t)| t.clone()) - .or_else(&uid_fallback); - if let Some(obj_type) = obj_type { + if df_qids.name_value_mut.contains(&callee_id) { + if let Some(obj_type) = get_obj_type(offset) { return Some(DynamicFieldInfo::singleton( obj_type, NameValueInfo::NameValue { @@ -340,11 +333,8 @@ fn collect_dynamic_field_info( } } - if dynamic_field_name_only_fun_qids.contains(&callee_id) { - let obj_type = get_uid_object_type(&uid_info, &alias_info, srcs[0], offset) - .map(|(_, t)| t.clone()) - .or_else(&uid_fallback); - if let Some(obj_type) = obj_type { + if df_qids.name_only.contains(&callee_id) { + if let Some(obj_type) = get_obj_type(offset) { return Some(DynamicFieldInfo::singleton( obj_type, NameValueInfo::NameOnly(type_inst[0].clone()), @@ -356,11 +346,7 @@ fn collect_dynamic_field_info( .get_callee_spec_qid(&builder.fun_env.get_qualified_id(), &callee_id) .unwrap_or(&callee_id); - let func_env = builder - .fun_env - .module_env - .env - .get_function(*fun_id_with_info); + let func_env = env.get_function(*fun_id_with_info); // native, reachable or intrinsic functions do not access dynamic fields if func_env.is_native() || get_info(&builder.get_target()).reachable { @@ -383,18 +369,6 @@ fn collect_dynamic_field_info( info.uid_info = uid_info; - // Check if any entries use UID type — if so, all bytecode df calls must use - // UID type to match the consolidated annotations from finalize. - let use_uid_type = builder - .fun_env - .module_env - .env - .uid_qid() - .map_or(false, |uid_qid| { - let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); - info.dynamic_field_mappings.contains_key(&uid_type) - }); - let code = std::mem::take(&mut builder.data.code); for (offset, bc) in code.into_iter().enumerate() { match bc { @@ -404,17 +378,19 @@ fn collect_dynamic_field_info( Operation::Function(module_id, fun_id, mut type_inst), mut srcs, aa, - ) if all_dynamic_field_fun_qids.contains(&module_id.qualified(fun_id)) => { - if use_uid_type { - // All entries consolidated under UID by finalize — bytecode must match - if let Some(uid_qid) = builder.fun_env.module_env.env.uid_qid() { - type_inst.push(Type::Datatype(uid_qid.module_id, uid_qid.id, vec![])); + ) if df_qids.all.contains(&module_id.qualified(fun_id)) => { + if use_uid { + // All df operations use UID type directly + if let Some(ref uid_ty) = uid_type { + type_inst.push(uid_ty.clone()); } } else if let Some((obj_local, obj_type)) = get_uid_object_type(&info.uid_info, &alias_info, srcs[0], offset) { srcs[0] = *obj_local; - if !dynamic_field_name_value_fun_mut_qids.contains(&module_id.qualified(fun_id)) + if !df_qids + .name_value_mut + .contains(&module_id.qualified(fun_id)) && builder.get_local_type(srcs[0]).is_mutable_reference() { srcs[0] = builder.emit_let_read_ref(srcs[0]); @@ -593,6 +569,66 @@ impl DynamicFieldAnalysisProcessor { } impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { + fn initialize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { + let uid_qid = match env.uid_qid() { + Some(qid) => qid, + None => return, + }; + let df_qids = collect_df_qids(env); + + // Check if any accessible function has a &UID parameter and makes df calls. + // This predicts whether uid_fallback warnings would be emitted during process, + // so we can route ALL df operations through UID type for consistency. + let mut use_uid = false; + for fun_id in targets.get_funs().collect_vec() { + let fun_env = env.get_function(fun_id); + if fun_env.is_native() || fun_env.is_intrinsic() { + continue; + } + let data = match targets.get_data(&fun_id, &FunctionVariant::Baseline) { + Some(d) => d, + None => continue, + }; + let target = FunctionTarget::new(&fun_env, data); + let info = get_info(&target); + if !info.accessible() { + continue; + } + + // Check if any parameter is of type &UID or &mut UID + let has_uid_param = (0..target.get_parameter_count()).any(|idx| { + let ty = target.get_local_type(idx); + ty.skip_reference() + .get_datatype() + .map_or(false, |(qid, _)| qid == uid_qid) + }); + if !has_uid_param { + continue; + } + + // Emit warning for each df call in this function + for bc in &data.code { + if let Bytecode::Call(attr_id, _, Operation::Function(mid, fid, _), _, _) = bc { + if df_qids.all.contains(&mid.qualified(*fid)) { + use_uid = true; + let loc = target.get_bytecode_loc(*attr_id); + env.add_diag( + Diagnostic::new(Severity::Warning) + .with_message( + "UID object type not found, dynamic fields will be placed under UID", + ) + .with_labels(vec![Label::primary(loc.file_id(), loc.span())]), + ); + } + } + } + } + + if use_uid { + env.set_extension(UseUidForDynamicFields); + } + } + fn process( &self, targets: &mut FunctionTargetsHolder, @@ -611,10 +647,21 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { return data; } + let use_uid = fun_env + .module_env + .env + .get_extension::() + .is_some(); + let mut builder = FunctionDataBuilder::new(fun_env, data); // Collect the dynamic field info - let info = collect_dynamic_field_info(targets, &mut builder, info.verified || info.inlined); + let info = collect_dynamic_field_info( + targets, + &mut builder, + info.verified || info.inlined, + use_uid, + ); builder .data @@ -644,17 +691,16 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { }), ); - // Propagate UID-keyed entries through the call graph. - // Generic functions with UID fallback (e.g. borrow_mut(&mut UID, ...)) - // have UID → {u64, T} entries with open types. Callee info propagation - // is skipped for reachable functions, so concrete instantiations from - // callers (e.g. borrow_mut) don't appear in annotations. - // This pass transitively instantiates UID entries through the call graph - // to collect concrete entries like UID → {u64, Validator}. if let Some(uid_qid) = env.uid_qid() { let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); - // Collect initial UID entries from ALL functions' annotations + // Propagate UID-keyed entries through the call graph. + // Generic functions with UID fallback (e.g. borrow_mut(&mut UID, ...)) + // have UID → {u64, T} entries with open types. Callee info propagation + // is skipped for reachable functions, so concrete instantiations from + // callers (e.g. borrow_mut) don't appear in annotations. + // This pass transitively instantiates UID entries through the call graph + // to collect concrete entries like UID → {u64, Validator}. let funs: Vec<_> = targets.get_funs().collect(); let mut uid_entries: BTreeMap<_, BTreeSet> = BTreeMap::new(); for &fun_id in &funs { @@ -669,10 +715,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } } - // If any function has UID-keyed entries (i.e., warnings were emitted), - // consolidate all dynamic field info under UID — both the global - // combined_info and each function's per-function annotation. - // This ensures all Boogie slots are consistent across the entire program. if !uid_entries.is_empty() { // Propagate: if a callee has UID entries, the caller inherits them // (instantiated with the call's type args). Repeat until fixpoint. @@ -732,40 +774,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { .or_insert_with(BTreeSet::new) .extend(concrete_entries); } - - // Consolidate global combined_info: merge all entries under UID type - let all_entries: BTreeSet = combined_info - .dynamic_field_mappings - .values() - .flat_map(|s| s.iter().cloned()) - .collect(); - combined_info.dynamic_field_mappings.clear(); - combined_info - .dynamic_field_mappings - .insert(uid_type.clone(), all_entries); - - // Consolidate each function's per-function annotation under UID - for &fun_id in &funs { - if let Some(data) = targets.get_data_mut(&fun_id, &FunctionVariant::Baseline) { - if let Some(info) = data.annotations.get::() { - if !info.dynamic_field_mappings.is_empty() - && !info.dynamic_field_mappings.keys().all(|k| k == &uid_type) - { - let mut new_info = info.clone(); - let all: BTreeSet = new_info - .dynamic_field_mappings - .values() - .flat_map(|s| s.iter().cloned()) - .collect(); - new_info.dynamic_field_mappings.clear(); - new_info - .dynamic_field_mappings - .insert(uid_type.clone(), all); - data.annotations.set(new_info, true); - } - } - } - } } // Remove open-typed UID entries. Generic functions create UID → {K, V} From cd32af166eceb57a5e569a5b5e9114cbfbedec02 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 03:00:09 +0000 Subject: [PATCH 12/21] =?UTF-8?q?refactor:=20simplify=20finalize=20?= =?UTF-8?q?=E2=80=94=20remove=20consolidation,=20keep=20fixpoint=20+=20ope?= =?UTF-8?q?n-type=20filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finalize now collects from all functions (like main) instead of just specs/pure/abort-check. Removed the per-function and global annotation consolidation (handled by initialize + process). Kept the fixpoint loop for UID entry propagation (needed when reachable functions skip callee propagation) and open-type filtering (prevents ill-founded Boogie types). Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 62177844fd..05a33f9270 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -673,34 +673,26 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { fn finalize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { // Collect and combine all functions' dynamic field info - let mut combined_info = DynamicFieldInfo::iter_union( - targets - .specs() - .copied() - .chain(targets.get_funs().filter(|fun_id| { - targets.is_pure_fun(fun_id) || targets.is_abort_check_fun(fun_id) - })) - .filter_map(|fun_id| { - targets - .get_data(&fun_id, &FunctionVariant::Baseline) - .and_then(|data| { - data.annotations - .get::() - .map(|info| info.clone()) - }) - }), - ); - + let mut combined_info = + DynamicFieldInfo::iter_union(targets.get_funs().filter_map(|fun_id| { + targets + .get_data(&fun_id, &FunctionVariant::Baseline) + .and_then(|data| { + data.annotations + .get::() + .map(|info| info.clone()) + }) + })); + + // Propagate UID-keyed entries through the call graph and filter open types. + // Generic functions taking &UID create UID → {K, T} with open type params. + // Reachable functions skip callee propagation in process, so concrete + // instantiations (e.g. UID → {u64, Validator}) don't flow through chains + // of reachable functions. This fixpoint loop instantiates them transitively. + // Open entries are then removed to avoid ill-founded Boogie types. if let Some(uid_qid) = env.uid_qid() { let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); - // Propagate UID-keyed entries through the call graph. - // Generic functions with UID fallback (e.g. borrow_mut(&mut UID, ...)) - // have UID → {u64, T} entries with open types. Callee info propagation - // is skipped for reachable functions, so concrete instantiations from - // callers (e.g. borrow_mut) don't appear in annotations. - // This pass transitively instantiates UID entries through the call graph - // to collect concrete entries like UID → {u64, Validator}. let funs: Vec<_> = targets.get_funs().collect(); let mut uid_entries: BTreeMap<_, BTreeSet> = BTreeMap::new(); for &fun_id in &funs { @@ -716,8 +708,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } if !uid_entries.is_empty() { - // Propagate: if a callee has UID entries, the caller inherits them - // (instantiated with the call's type args). Repeat until fixpoint. loop { let mut changed = false; for &fun_id in &funs { @@ -760,7 +750,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } } - // Add concrete UID entries to combined info let concrete_entries: BTreeSet = uid_entries .values() .flat_map(|entries| entries.iter()) @@ -776,10 +765,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } } - // Remove open-typed UID entries. Generic functions create UID → {K, V} - // with unresolved type params that cause ill-founded Boogie types. - // Non-UID entries (e.g. Versioned → {u64, T}) are kept as the Boogie - // backend handles them correctly for generic spec functions. combined_info .dynamic_field_mappings .retain(|ty, name_values| { @@ -789,7 +774,7 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { return !name_values.is_empty(); } } - !ty.is_open() + true }); } From e7a37f61dd56a965d5dd1a19d2170f961aacdfb3 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 03:29:37 +0000 Subject: [PATCH 13/21] refactor: remove reachable skip and fixpoint loop in dynamic field analysis Allow callee info propagation for reachable functions so generic instantiation flows naturally through call chains. This eliminates the need for the fixpoint loop in finalize. Gracefully skip callees that were filtered out (spec functions) instead of panicking. Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 93 +------------------ 1 file changed, 5 insertions(+), 88 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 05a33f9270..d1efa3f316 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -348,19 +348,13 @@ fn collect_dynamic_field_info( let func_env = env.get_function(*fun_id_with_info); - // native, reachable or intrinsic functions do not access dynamic fields - if func_env.is_native() || get_info(&builder.get_target()).reachable { + if func_env.is_native() { return None; } let info = targets .get_data(fun_id_with_info, &FunctionVariant::Baseline) - .map(|data| get_fun_info(data)) - .expect(&format!( - "callee `{}` of `{}` was filtered out", - func_env.get_full_name_str(), - builder.fun_env.get_full_name_str() - )); + .map(|data| get_fun_info(data))?; Some(info.instantiate(type_inst)) } _ => None, @@ -684,87 +678,10 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { }) })); - // Propagate UID-keyed entries through the call graph and filter open types. - // Generic functions taking &UID create UID → {K, T} with open type params. - // Reachable functions skip callee propagation in process, so concrete - // instantiations (e.g. UID → {u64, Validator}) don't flow through chains - // of reachable functions. This fixpoint loop instantiates them transitively. - // Open entries are then removed to avoid ill-founded Boogie types. + // Remove open-typed UID entries. Generic functions taking &UID create + // UID → {K, T} with open type params that cause ill-founded Boogie types. + // Concrete instantiations flow through callee propagation in process. if let Some(uid_qid) = env.uid_qid() { - let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); - - let funs: Vec<_> = targets.get_funs().collect(); - let mut uid_entries: BTreeMap<_, BTreeSet> = BTreeMap::new(); - for &fun_id in &funs { - if let Some(data) = targets.get_data(&fun_id, &FunctionVariant::Baseline) { - if let Some(info) = data.annotations.get::() { - if let Some(entries) = info.dynamic_field_mappings.get(&uid_type) { - if !entries.is_empty() { - uid_entries.insert(fun_id, entries.clone()); - } - } - } - } - } - - if !uid_entries.is_empty() { - loop { - let mut changed = false; - for &fun_id in &funs { - if let Some(data) = targets.get_data(&fun_id, &FunctionVariant::Baseline) { - let mut new_entries = BTreeSet::new(); - for bc in &data.code { - if let Bytecode::Call( - _, - _, - Operation::Function(mid, fid, type_inst), - _, - _, - ) = bc - { - let callee_id = mid.qualified(*fid); - if callee_id == fun_id { - continue; - } - if let Some(callee_entries) = - uid_entries.get(&callee_id).cloned() - { - for nv in &callee_entries { - new_entries.insert(nv.instantiate(type_inst)); - } - } - } - } - if !new_entries.is_empty() { - let entry = uid_entries.entry(fun_id).or_insert_with(BTreeSet::new); - let old_len = entry.len(); - entry.extend(new_entries); - if entry.len() > old_len { - changed = true; - } - } - } - } - if !changed { - break; - } - } - - let concrete_entries: BTreeSet = uid_entries - .values() - .flat_map(|entries| entries.iter()) - .filter(|nv| !nv.is_open()) - .cloned() - .collect(); - if !concrete_entries.is_empty() { - combined_info - .dynamic_field_mappings - .entry(uid_type.clone()) - .or_insert_with(BTreeSet::new) - .extend(concrete_entries); - } - } - combined_info .dynamic_field_mappings .retain(|ty, name_values| { From b95eec76e15239366e614be263c3c289f2733ad5 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 03:42:58 +0000 Subject: [PATCH 14/21] refactor: use Cell field instead of env extension for use_uid flag Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index d1efa3f316..c57d2af355 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -18,14 +18,11 @@ use move_model::{ ty::Type, }; use std::{ + cell::Cell, collections::{BTreeMap, BTreeSet}, rc::Rc, }; -/// When set as an env extension, signals that all dynamic field operations -/// should use UID type instead of the parent object type. -struct UseUidForDynamicFields; - /// Stores dynamic field type information for a specific function #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct DynamicFieldInfo { @@ -554,11 +551,15 @@ fn get_uid_object_type<'a>( }) } -pub struct DynamicFieldAnalysisProcessor(); +pub struct DynamicFieldAnalysisProcessor { + use_uid: Cell, +} impl DynamicFieldAnalysisProcessor { pub fn new() -> Box { - Box::new(Self()) + Box::new(Self { + use_uid: Cell::new(false), + }) } } @@ -618,9 +619,7 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { } } - if use_uid { - env.set_extension(UseUidForDynamicFields); - } + self.use_uid.set(use_uid); } fn process( @@ -641,11 +640,7 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { return data; } - let use_uid = fun_env - .module_env - .env - .get_extension::() - .is_some(); + let use_uid = self.use_uid.get(); let mut builder = FunctionDataBuilder::new(fun_env, data); From e3a439f92c497161346f15cdadd4266ce2f2cd24 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 04:36:33 +0000 Subject: [PATCH 15/21] refactor: replace has_uid_param heuristic with compute_uid_info_local Instead of checking if a function has a UID parameter, actually try resolving each df call's parent object type using the same logic as compute_uid_info (BorrowField/GetField + callee propagation). Only flag as unresolvable when resolution genuinely fails. Callee uid_info is computed recursively on-the-fly since annotations don't exist yet in initialize. Co-Authored-By: Claude Opus 4.6 --- .../src/dynamic_field_analysis.rs | 109 ++++++++++++++---- 1 file changed, 87 insertions(+), 22 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index c57d2af355..b6bb049d16 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -345,7 +345,7 @@ fn collect_dynamic_field_info( let func_env = env.get_function(*fun_id_with_info); - if func_env.is_native() { + if func_env.is_native() || func_env.is_intrinsic() { return None; } @@ -403,6 +403,79 @@ fn collect_dynamic_field_info( info } +/// Like `compute_uid_info` but runs before `process` (no callee annotations available). +/// Computes callee uid_info on-the-fly instead of reading annotations. +fn compute_uid_info_local( + fun_target: &FunctionTarget, + targets: &FunctionTargetsHolder, + code: &[Bytecode], +) -> BTreeMap { + let env = fun_target.global_env(); + code.iter() + .filter_map(|bc| match bc { + Bytecode::Call( + attr_id, + dests, + Operation::BorrowField(mid, sid, tys, offset), + srcs, + _, + ) if is_uid_field_access(&env.get_struct(mid.qualified(*sid)), *offset) + && !dests.is_empty() => + { + Some(( + dests[0], + (srcs[0], Type::Datatype(*mid, *sid, tys.clone()), *attr_id), + )) + } + Bytecode::Call(attr_id, dests, Operation::GetField(mid, sid, tys, offset), srcs, _) + if is_uid_field_access(&env.get_struct(mid.qualified(*sid)), *offset) + && !dests.is_empty() => + { + Some(( + dests[0], + (srcs[0], Type::Datatype(*mid, *sid, tys.clone()), *attr_id), + )) + } + Bytecode::Call(attr_id, dests, Operation::Function(mid, fid, tys), srcs, _) + if !dests.is_empty() => + { + let callee_id = mid.qualified(*fid); + if callee_id == fun_target.global_env().type_inv_qid() { + return None; + } + + let callee_data = targets.get_data(&callee_id, &FunctionVariant::Baseline)?; + let callee_env = env.get_function(callee_id); + let callee_target = FunctionTarget::new(&callee_env, callee_data); + let callee_uid_info = + compute_uid_info_local(&callee_target, targets, &callee_data.code); + + for key in callee_uid_info.keys() { + if let Some(ret_pos) = get_function_return_local_pos(*key, &callee_data.code) { + let (_, obj_type) = callee_uid_info.get(key).unwrap(); + return Some(( + dests[ret_pos], + (srcs[0], obj_type.instantiate(tys), *attr_id), + )); + } + } + + None + } + _ => None, + }) + .into_group_map() + .into_iter() + .filter_map(|(temp_idx, types)| { + if types.len() == 1 { + Some((temp_idx, (types[0].0, types[0].1.clone()))) + } else { + None + } + }) + .collect() +} + /// Computes a mapping from temporary indices to the objects and types of objects they reference fn compute_uid_info( fun_target: &FunctionTarget, @@ -565,15 +638,14 @@ impl DynamicFieldAnalysisProcessor { impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { fn initialize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { - let uid_qid = match env.uid_qid() { - Some(qid) => qid, - None => return, - }; + if env.uid_qid().is_none() { + return; + } let df_qids = collect_df_qids(env); - // Check if any accessible function has a &UID parameter and makes df calls. - // This predicts whether uid_fallback warnings would be emitted during process, - // so we can route ALL df operations through UID type for consistency. + // Check if any accessible function has a df call where the parent + // object type can't be resolved locally. When that happens, fall back + // to UID type for ALL df operations for consistency. let mut use_uid = false; for fun_id in targets.get_funs().collect_vec() { let fun_env = env.get_function(fun_id); @@ -590,21 +662,14 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { continue; } - // Check if any parameter is of type &UID or &mut UID - let has_uid_param = (0..target.get_parameter_count()).any(|idx| { - let ty = target.get_local_type(idx); - ty.skip_reference() - .get_datatype() - .map_or(false, |(qid, _)| qid == uid_qid) - }); - if !has_uid_param { - continue; - } + let uid_info = compute_uid_info_local(&target, targets, &data.code); + let alias_info = ReachingDefProcessor::analyze_reaching_definitions(&fun_env, data); - // Emit warning for each df call in this function - for bc in &data.code { - if let Bytecode::Call(attr_id, _, Operation::Function(mid, fid, _), _, _) = bc { - if df_qids.all.contains(&mid.qualified(*fid)) { + for (offset, bc) in data.code.iter().enumerate() { + if let Bytecode::Call(attr_id, _, Operation::Function(mid, fid, _), srcs, _) = bc { + if df_qids.all.contains(&mid.qualified(*fid)) + && get_uid_object_type(&uid_info, &alias_info, srcs[0], offset).is_none() + { use_uid = true; let loc = target.get_bytecode_loc(*attr_id); env.add_diag( From 662444c33bd44534f5eca1f77d513b7fc6ce01d0 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 06:11:55 +0000 Subject: [PATCH 16/21] refactor: clarify finalize open-type filter comment Co-Authored-By: Claude Opus 4.6 --- crates/move-stackless-bytecode/src/dynamic_field_analysis.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index b6bb049d16..908a393ef8 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -740,7 +740,8 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { // Remove open-typed UID entries. Generic functions taking &UID create // UID → {K, T} with open type params that cause ill-founded Boogie types. - // Concrete instantiations flow through callee propagation in process. + // These exist in per-function info so callee propagation can instantiate them + // concretely, but must not reach the global combined info sent to Boogie. if let Some(uid_qid) = env.uid_qid() { combined_info .dynamic_field_mappings From 1037192edc1615b1caf54f27b071cf2cb98b446a Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 19:09:28 +0000 Subject: [PATCH 17/21] fix: avoid circular UID datatype in Boogie by skipping UID-wrapping for type params used in UID dynamic fields Instead of filtering open-typed UID entries in dynamic field analysis finalize, prevent the circularity at the source: don't generate UID-wrapping datatype declarations for type parameters that appear in UID's dynamic field entries. Co-Authored-By: Claude Opus 4.6 --- .../src/boogie_backend/bytecode_translator.rs | 29 +++++++++++++++++-- .../src/dynamic_field_analysis.rs | 20 +------------ .../dynamic_field/issue-237.fail.move.snap | 1 - 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs index 14a855d220..64458aaca1 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs @@ -388,6 +388,30 @@ impl<'env> BoogieTranslator<'env> { } // Add given type declarations for type parameters. + // Collect type param indices that appear in UID's dynamic field entries. + // These cannot be declared as datatypes containing UID (would create a cycle). + let uid_df_type_params: BTreeSet = env + .uid_qid() + .map(|uid_qid| { + let uid_type = Type::Datatype(uid_qid.module_id, uid_qid.id, vec![]); + let df_info = dynamic_field_analysis::get_env_info(env); + let mut params = BTreeSet::new(); + for (name, value) in df_info.dynamic_field_names_values(&uid_type) { + name.visit(&mut |t| { + if let Type::TypeParameter(idx) = t { + params.insert(*idx); + } + }); + value.visit(&mut |t| { + if let Type::TypeParameter(idx) = t { + params.insert(*idx); + } + }); + } + params + }) + .unwrap_or_default(); + emitln!(writer, "\n\n// Given Types for Type Parameters\n"); for idx in &mono_info.type_params { let param_type = boogie_type_param(env, *idx); @@ -397,9 +421,10 @@ impl<'env> BoogieTranslator<'env> { .find_datatype_by_tag(&StructTag::from_str("0x2::object::UID").unwrap()) .and_then(|uid_qid| mono_info.structs.get(&uid_qid)) .is_some(); - if is_uid { + if is_uid && !uid_df_type_params.contains(idx) { // Sui-specific to allow "using" unresolved type params as Sui objects in Boogie - // (otherwise Boogie compilation errors may occur) + // (otherwise Boogie compilation errors may occur). + // Skip if this type param appears in UID's dynamic fields to avoid circularity. emitln!(writer, "datatype {} {{", param_type); emitln!(writer, " {}($id: $2_object_UID)", param_type); emitln!(writer, "}"); diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index 11bbbe6eff..eda5f1c12b 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -727,7 +727,7 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { fn finalize(&self, env: &GlobalEnv, targets: &mut FunctionTargetsHolder) { // Collect and combine all functions' dynamic field info - let mut combined_info = DynamicFieldInfo::iter_union( + let combined_info = DynamicFieldInfo::iter_union( targets .specs() .copied() @@ -745,24 +745,6 @@ impl FunctionTargetProcessor for DynamicFieldAnalysisProcessor { }), ); - // Remove open-typed UID entries. Generic functions taking &UID create - // UID → {K, T} with open type params that cause ill-founded Boogie types. - // These exist in per-function info so callee propagation can instantiate them - // concretely, but must not reach the global combined info sent to Boogie. - if let Some(uid_qid) = env.uid_qid() { - combined_info - .dynamic_field_mappings - .retain(|ty, name_values| { - if let Some((qid, _)) = ty.get_datatype() { - if qid == uid_qid { - name_values.retain(|nv| !nv.is_open()); - return !name_values.is_empty(); - } - } - true - }); - } - // Set the combined info in the environment env.set_extension(combined_info); } diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap index aea30d236c..b8c67ad7f3 100644 --- a/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap +++ b/crates/sui-prover/tests/snapshots/dynamic_field/issue-237.fail.move.snap @@ -1,6 +1,5 @@ --- source: crates/sui-prover/tests/integration.rs -assertion_line: 297 expression: output --- Verification successful From b16753913fcabbb3d0a83dda89e84a7a8658229b Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Fri, 13 Mar 2026 19:12:25 +0000 Subject: [PATCH 18/21] remove unused is_open method from NameValueInfo Co-Authored-By: Claude Opus 4.6 --- .../move-stackless-bytecode/src/dynamic_field_analysis.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs index eda5f1c12b..273e9d3255 100644 --- a/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs +++ b/crates/move-stackless-bytecode/src/dynamic_field_analysis.rs @@ -59,13 +59,6 @@ impl NameValueInfo { } } - pub fn is_open(&self) -> bool { - match self { - NameValueInfo::NameValue { name, value, .. } => name.is_open() || value.is_open(), - NameValueInfo::NameOnly(name) => name.is_open(), - } - } - pub fn instantiate(&self, type_inst: &[Type]) -> Self { match self { NameValueInfo::NameValue { From a4d8cd305ff1a409708930c680c0197eed1d3679 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Mon, 16 Mar 2026 01:43:04 +0000 Subject: [PATCH 19/21] fix: parenthesize compound Boogie types in dynamic field Table declarations When a dynamic field value type is a compound type like Vec(T), it must be parenthesized inside Table declarations to avoid Boogie parsing it as extra type arguments. Co-Authored-By: Claude Opus 4.6 --- .../src/boogie_backend/bytecode_translator.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs index 64458aaca1..6441315c6e 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs @@ -1472,10 +1472,16 @@ impl<'env> StructTranslator<'env> { ) }); let dynamic_fields = dynamic_field_names_values.iter().map(|(name, value)| { + let value_type = boogie_type(env, value); + let value_type = if value_type.contains(' ') { + format!("({})", value_type) + } else { + value_type + }; format!( "{}: (Table int {})", boogie_dynamic_field_sel(self.parent.env, name, value), - boogie_type(env, value), + value_type, ) }); let all_fields = fields.chain(dynamic_fields).join(", "); @@ -1522,12 +1528,18 @@ impl<'env> StructTranslator<'env> { ); } for (pos, (name, value)) in dynamic_field_names_values.iter().enumerate() { + let value_type = boogie_type(env, value); + let value_type = if value_type.contains(' ') { + format!("({})", value_type) + } else { + value_type + }; self.emit_function( &format!( "{}(s: {}, x: (Table int {})): {}", boogie_dynamic_field_update(struct_env, self.type_inst, name, value), struct_name, - boogie_type(env, value), + value_type, struct_name ), || { From 602a1eae8e8845c2ed6567dd05bd302a535d6bc8 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Mon, 16 Mar 2026 02:16:56 +0000 Subject: [PATCH 20/21] test: add tests for UID fallback, circular datatype fix, and compound type parenthesization - uid_param_vec_value: UID fallback + vector value type (parenthesization) - uid_param_multi_type_params: multiple type params on &UID (circular fix) - uid_param_multi_ops: multiple df operations with different types on same &UID - uid_param_nested_generic: generic calling generic on &UID (transitive fallback) - vec_value_concrete: vector value type on normal object (parenthesization) Co-Authored-By: Claude Opus 4.6 --- .../dynamic_field/uid_param_multi_ops.ok.move | 26 +++++++++++++++++ .../uid_param_multi_type_params.ok.move | 24 +++++++++++++++ .../uid_param_nested_generic.ok.move | 29 +++++++++++++++++++ .../dynamic_field/uid_param_vec_value.ok.move | 20 +++++++++++++ .../dynamic_field/vec_value_concrete.ok.move | 20 +++++++++++++ .../uid_param_multi_ops.ok.move.snap | 16 ++++++++++ .../uid_param_multi_type_params.ok.move.snap | 10 +++++++ .../uid_param_nested_generic.ok.move.snap | 16 ++++++++++ .../uid_param_vec_value.ok.move.snap | 10 +++++++ .../vec_value_concrete.ok.move.snap | 5 ++++ 10 files changed, 176 insertions(+) create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_ops.ok.move create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_type_params.ok.move create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_nested_generic.ok.move create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/uid_param_vec_value.ok.move create mode 100644 crates/sui-prover/tests/inputs/dynamic_field/vec_value_concrete.ok.move create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_ops.ok.move.snap create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_type_params.ok.move.snap create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_nested_generic.ok.move.snap create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/uid_param_vec_value.ok.move.snap create mode 100644 crates/sui-prover/tests/snapshots/dynamic_field/vec_value_concrete.ok.move.snap diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_ops.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_ops.ok.move new file mode 100644 index 0000000000..1e8887bffe --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_ops.ok.move @@ -0,0 +1,26 @@ +module 0x42::foo; + +use prover::prover::{requires, ensures}; +use sui::dynamic_field as df; + +public struct NameKey has copy, store, drop {} +public struct CountKey has copy, store, drop {} + +public struct MyObject has key { + id: UID, +} + +public fun initialize(uid: &mut UID, count: u64) { + df::add(uid, NameKey {}, true); + df::add(uid, CountKey {}, count); +} + +#[spec(prove)] +fun verify_initialize(obj: &mut MyObject, count: u64) { + requires(!df::exists_with_type(&obj.id, NameKey {})); + requires(!df::exists_with_type(&obj.id, CountKey {})); + initialize(&mut obj.id, count); + ensures(df::exists_with_type(&obj.id, NameKey {})); + ensures(df::exists_with_type(&obj.id, CountKey {})); + ensures(*df::borrow(&obj.id, CountKey {}) == count); +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_type_params.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_type_params.ok.move new file mode 100644 index 0000000000..ccf6829a72 --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_multi_type_params.ok.move @@ -0,0 +1,24 @@ +module 0x42::foo; + +use prover::prover::{requires, ensures}; +use sui::dynamic_field as df; + +public struct MyObject has key { + id: UID, +} + +public fun generic_add(uid: &mut UID, key: K, val: V) { + df::add(uid, key, val); +} + +public fun generic_exists(uid: &UID, key: K): bool { + df::exists_with_type(uid, key) +} + +#[spec(prove)] +fun generic_add_u64_bool_spec(obj: &mut MyObject, key: u64, val: bool) { + requires(!df::exists_with_type(&obj.id, key)); + generic_add(&mut obj.id, key, val); + ensures(df::exists_with_type(&obj.id, key)); + ensures(*df::borrow(&obj.id, key) == val); +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_nested_generic.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_nested_generic.ok.move new file mode 100644 index 0000000000..27e1e6563a --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_nested_generic.ok.move @@ -0,0 +1,29 @@ +module 0x42::foo; + +use prover::prover::{requires, ensures}; +use sui::dynamic_field as df; + +public struct MyObject has key { + id: UID, +} + +public fun has_field(uid: &UID, key: K): bool { + df::exists_with_type(uid, key) +} + +public fun get_field(uid: &UID, key: K, default: V): V { + if (has_field(uid, key)) { + *df::borrow(uid, key) + } else { + default + } +} + +#[spec(prove)] +fun verify_get_field(obj: &MyObject, key: u64): u64 { + requires(df::exists_with_type(&obj.id, key)); + requires(*df::borrow(&obj.id, key) == 42); + let result = get_field(&obj.id, key, 0); + ensures(result == 42); + result +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/uid_param_vec_value.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_vec_value.ok.move new file mode 100644 index 0000000000..8f6b045403 --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/uid_param_vec_value.ok.move @@ -0,0 +1,20 @@ +module 0x42::foo; + +use prover::prover::{requires, ensures}; +use sui::dynamic_field as df; + +public struct MyObject has key { + id: UID, +} + +public fun set_scores(uid: &mut UID, key: u64, scores: vector) { + df::add(uid, key, scores); +} + +#[spec(prove)] +fun verify_set_scores(obj: &mut MyObject, key: u64, scores: vector) { + requires(!df::exists_with_type>(&obj.id, key)); + set_scores(&mut obj.id, key, scores); + ensures(df::exists_with_type>(&obj.id, key)); + ensures(*df::borrow>(&obj.id, key) == scores); +} diff --git a/crates/sui-prover/tests/inputs/dynamic_field/vec_value_concrete.ok.move b/crates/sui-prover/tests/inputs/dynamic_field/vec_value_concrete.ok.move new file mode 100644 index 0000000000..45112b4292 --- /dev/null +++ b/crates/sui-prover/tests/inputs/dynamic_field/vec_value_concrete.ok.move @@ -0,0 +1,20 @@ +module 0x42::foo; + +use prover::prover::{requires, ensures}; +use sui::dynamic_field as df; + +public struct Foo has key { + id: UID, +} + +fun store_list(x: &mut Foo, key: u64, items: vector) { + df::add>(&mut x.id, key, items); +} + +#[spec(prove)] +fun store_list_spec(x: &mut Foo, key: u64, items: vector) { + requires(!df::exists_with_type>(&x.id, key)); + store_list(x, key, items); + ensures(df::exists_with_type>(&x.id, key)); + ensures(*df::borrow>(&x.id, key) == items); +} diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_ops.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_ops.ok.move.snap new file mode 100644 index 0000000000..6df5076c57 --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_ops.ok.move.snap @@ -0,0 +1,16 @@ +--- +source: crates/sui-prover/tests/integration.rs +expression: output +--- +Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_multi_ops.ok.move:14:5 + │ +14 │ df::add(uid, NameKey {}, true); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_multi_ops.ok.move:15:5 + │ +15 │ df::add(uid, CountKey {}, count); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_type_params.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_type_params.ok.move.snap new file mode 100644 index 0000000000..5583eded25 --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_multi_type_params.ok.move.snap @@ -0,0 +1,10 @@ +--- +source: crates/sui-prover/tests/integration.rs +expression: output +--- +Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_multi_type_params.ok.move:11:5 + │ +11 │ df::add(uid, key, val); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_nested_generic.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_nested_generic.ok.move.snap new file mode 100644 index 0000000000..b467181011 --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_nested_generic.ok.move.snap @@ -0,0 +1,16 @@ +--- +source: crates/sui-prover/tests/integration.rs +expression: output +--- +Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_nested_generic.ok.move:16:10 + │ +16 │ *df::borrow(uid, key) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_nested_generic.ok.move:11:5 + │ +11 │ df::exists_with_type(uid, key) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_vec_value.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_vec_value.ok.move.snap new file mode 100644 index 0000000000..94125bc8ee --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/uid_param_vec_value.ok.move.snap @@ -0,0 +1,10 @@ +--- +source: crates/sui-prover/tests/integration.rs +expression: output +--- +Verification successful +warning: UID object type not found, dynamic fields will be placed under UID + ┌─ tests/inputs/dynamic_field/uid_param_vec_value.ok.move:11:5 + │ +11 │ df::add(uid, key, scores); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/sui-prover/tests/snapshots/dynamic_field/vec_value_concrete.ok.move.snap b/crates/sui-prover/tests/snapshots/dynamic_field/vec_value_concrete.ok.move.snap new file mode 100644 index 0000000000..95d8a7083e --- /dev/null +++ b/crates/sui-prover/tests/snapshots/dynamic_field/vec_value_concrete.ok.move.snap @@ -0,0 +1,5 @@ +--- +source: crates/sui-prover/tests/integration.rs +expression: output +--- +Verification successful From 54d49385c5f97cc2c1f52224bb47d01bb38cf279 Mon Sep 17 00:00:00 2001 From: Andrei Stefanescu Date: Mon, 16 Mar 2026 02:23:21 +0000 Subject: [PATCH 21/21] refactor: move dynamic field opaqueness check into is_used_datatype Structs with dynamic fields must not be opaque. Move this check from the struct translation loop into MonoInfo::is_used_datatype so the logic is centralized. Co-Authored-By: Claude Opus 4.6 --- .../src/boogie_backend/bytecode_translator.rs | 23 ++---- .../src/boogie_backend/lib.rs | 76 ++++++++++--------- .../src/mono_analysis.rs | 11 +++ 3 files changed, 60 insertions(+), 50 deletions(-) diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs index 6441315c6e..4d87e21748 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/bytecode_translator.rs @@ -484,26 +484,16 @@ impl<'env> BoogieTranslator<'env> { if !translated_types.insert(struct_name) { continue; } - // Check if this struct type has dynamic fields - if so, it should not be opaque - let struct_type = Type::Datatype( - struct_env.module_env.get_id(), - struct_env.get_id(), - type_inst.to_vec(), - ); - let has_dynamic_fields = dynamic_field_analysis::get_env_info(self.env) - .dynamic_field_names_values(&struct_type) - .next() - .is_some(); StructTranslator { parent: self, struct_env, type_inst: type_inst.as_slice(), - is_opaque: !has_dynamic_fields - && !mono_info.is_used_datatype( - self.env, - self.targets, - &struct_env.get_qualified_id(), - ), + is_opaque: !mono_info.is_used_datatype( + self.env, + self.targets, + &struct_env.get_qualified_id(), + type_inst, + ), } .translate(); } @@ -527,6 +517,7 @@ impl<'env> BoogieTranslator<'env> { self.env, self.targets, &enum_env.get_qualified_id(), + type_inst, ), } .translate(); diff --git a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs index e0b8a58778..b91898bd44 100644 --- a/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs +++ b/crates/move-prover-boogie-backend/src/boogie_backend/lib.rs @@ -230,12 +230,12 @@ pub fn add_prelude( .collect_vec(); let mut table_instances = vec![]; if let Some(table_qid) = env.table_qid() { - if mono_info.is_used_datatype(env, targets, &table_qid) { + if mono_info.is_used_datatype(env, targets, &table_qid, &[]) { table_instances.push(TableImpl::table(env, options, &mono_info, table_qid, false)); } } if let Some(object_table_qid) = env.object_table_qid() { - if mono_info.is_used_datatype(env, targets, &object_table_qid) { + if mono_info.is_used_datatype(env, targets, &object_table_qid, &[]) { table_instances.push(TableImpl::object_table( env, options, @@ -251,7 +251,7 @@ pub fn add_prelude( let (struct_qid, type_inst) = info.0.get_datatype().unwrap(); let is_uid_type = uid_qid.as_ref().is_some_and(|uid| *uid == struct_qid); if is_uid_type - || (mono_info.is_used_datatype(env, targets, &struct_qid) + || (mono_info.is_used_datatype(env, targets, &struct_qid, &[]) && mono_info .structs .get(&struct_qid) @@ -307,7 +307,7 @@ pub fn add_prelude( .find_struct(env.symbol_pool().make("Option")) .unwrap(); let option_instances = - if mono_info.is_used_datatype(env, targets, &option_env.get_qualified_id()) { + if mono_info.is_used_datatype(env, targets, &option_env.get_qualified_id(), &[]) { mono_info .structs .get(&option_env.get_qualified_id()) @@ -325,18 +325,22 @@ pub fn add_prelude( let vec_set_struct_env = vec_set_module_env .find_struct(env.symbol_pool().make("VecSet")) .unwrap(); - let vec_set_instances = - if mono_info.is_used_datatype(env, targets, &vec_set_struct_env.get_qualified_id()) { - mono_info - .structs - .get(&vec_set_struct_env.get_qualified_id()) - .unwrap_or(&BTreeSet::new()) - .iter() - .map(|tys| TypeInfo::new(env, options, &tys[0], false)) - .collect_vec() - } else { - vec![] - }; + let vec_set_instances = if mono_info.is_used_datatype( + env, + targets, + &vec_set_struct_env.get_qualified_id(), + &[], + ) { + mono_info + .structs + .get(&vec_set_struct_env.get_qualified_id()) + .unwrap_or(&BTreeSet::new()) + .iter() + .map(|tys| TypeInfo::new(env, options, &tys[0], false)) + .collect_vec() + } else { + vec![] + }; context.insert("vec_set_instances", &vec_set_instances); } @@ -344,23 +348,27 @@ pub fn add_prelude( let vec_map_struct_env = vec_map_module_env .find_struct(env.symbol_pool().make("VecMap")) .unwrap(); - let vec_map_instances = - if mono_info.is_used_datatype(env, targets, &vec_map_struct_env.get_qualified_id()) { - mono_info - .structs - .get(&vec_map_struct_env.get_qualified_id()) - .unwrap_or(&BTreeSet::new()) - .iter() - .map(|tys| { - ( - TypeInfo::new(env, options, &tys[0], false), - TypeInfo::new(env, options, &tys[1], false), - ) - }) - .collect_vec() - } else { - vec![] - }; + let vec_map_instances = if mono_info.is_used_datatype( + env, + targets, + &vec_map_struct_env.get_qualified_id(), + &[], + ) { + mono_info + .structs + .get(&vec_map_struct_env.get_qualified_id()) + .unwrap_or(&BTreeSet::new()) + .iter() + .map(|tys| { + ( + TypeInfo::new(env, options, &tys[0], false), + TypeInfo::new(env, options, &tys[1], false), + ) + }) + .collect_vec() + } else { + vec![] + }; context.insert("vec_map_instances", &vec_map_instances); } @@ -370,7 +378,7 @@ pub fn add_prelude( .find_struct(env.symbol_pool().make("TableVec")) .unwrap(); let table_vec_instances = - if mono_info.is_used_datatype(env, targets, &table_vec_env.get_qualified_id()) { + if mono_info.is_used_datatype(env, targets, &table_vec_env.get_qualified_id(), &[]) { mono_info .structs .get(&table_vec_env.get_qualified_id()) diff --git a/crates/move-stackless-bytecode/src/mono_analysis.rs b/crates/move-stackless-bytecode/src/mono_analysis.rs index 8a6740f960..cf24dda5be 100644 --- a/crates/move-stackless-bytecode/src/mono_analysis.rs +++ b/crates/move-stackless-bytecode/src/mono_analysis.rs @@ -61,7 +61,18 @@ impl MonoInfo { env: &GlobalEnv, targets: &FunctionTargetsHolder, dt_qid: &QualifiedId, + type_inst: &[Type], ) -> bool { + // A struct with dynamic fields must not be opaque + let struct_type = Type::Datatype(dt_qid.module_id, dt_qid.id, type_inst.to_vec()); + if dynamic_field_analysis::get_env_info(env) + .dynamic_field_names_values(&struct_type) + .next() + .is_some() + { + return true; + } + if env .get_extension::() .unwrap()