From 3b60c7c49fcb6c6f3afe31ad4f89f5a7329a5334 Mon Sep 17 00:00:00 2001 From: Anders Musikka Date: Mon, 29 Apr 2024 00:26:19 +0200 Subject: [PATCH] feat: savefile-abi (#41) * feat: savefile-abi Various fixes for the upcoming 0.17 release. --- .github/workflows/rust.yml | 9 +- Cargo.lock | 37 +- Cargo.toml | 5 +- README.md | 8 + compile_tests/Cargo.lock | 753 ++++++++++++++++++ compile_tests/Cargo.toml | 19 + compile_tests/src/main.rs | 3 + .../tests/compile-fail/abi_ref_return.rs | 18 + .../compile-fail/method_with_lifetimes.rs | 18 + .../tests/compile-fail/missing_self.rs | 18 + .../tests/compile-fail/no_impl_trait.rs | 18 + .../tests/compile-fail/no_mut_data_ref.rs | 18 + .../tests/compile-fail/no_mutable_fn.rs | 18 + .../tests/compile-fail/no_ref_box.rs | 18 + .../tests/compile-fail/no_ref_ref.rs | 18 + .../tests/compile-fail/no_return_ref_str.rs | 18 + .../tests/compile-fail/no_unmutable_fnmut.rs | 18 + compile_tests/tests/run-pass/do_nothing.rs | 2 + compile_tests/tests/tests.rs | 31 + release-plz.toml | 6 + savefile-abi-min-lib-impl/src/lib.rs | 3 +- savefile-abi-min/src/main.rs | 2 +- savefile-abi/CHANGELOG.md | 5 + savefile-abi/Cargo.toml | 6 +- savefile-abi/src/lib.rs | 138 ++-- savefile-derive/CHANGELOG.md | 9 +- savefile-derive/Cargo.toml | 4 +- savefile-derive/src/common.rs | 53 +- savefile-derive/src/deserialize.rs | 17 +- savefile-derive/src/lib.rs | 208 +++-- savefile-derive/src/savefile_abi.rs | 650 ++++++++------- savefile-derive/src/serialize.rs | 7 +- savefile-min-build/Cargo.toml | 2 +- savefile-min-build/src/lib.rs | 32 +- savefile-test/Cargo.toml | 4 +- savefile-test/src/lib.rs | 27 +- .../advanced_datatypes_test.rs | 50 +- .../src/savefile_abi_test/basic_abi_tests.rs | 16 +- savefile.sublime-project | 8 - savefile/CHANGELOG.md | 13 +- savefile/Cargo.toml | 6 +- savefile/src/lib.rs | 113 ++- 42 files changed, 1850 insertions(+), 576 deletions(-) create mode 100644 compile_tests/Cargo.lock create mode 100644 compile_tests/Cargo.toml create mode 100644 compile_tests/src/main.rs create mode 100644 compile_tests/tests/compile-fail/abi_ref_return.rs create mode 100644 compile_tests/tests/compile-fail/method_with_lifetimes.rs create mode 100644 compile_tests/tests/compile-fail/missing_self.rs create mode 100644 compile_tests/tests/compile-fail/no_impl_trait.rs create mode 100644 compile_tests/tests/compile-fail/no_mut_data_ref.rs create mode 100644 compile_tests/tests/compile-fail/no_mutable_fn.rs create mode 100644 compile_tests/tests/compile-fail/no_ref_box.rs create mode 100644 compile_tests/tests/compile-fail/no_ref_ref.rs create mode 100644 compile_tests/tests/compile-fail/no_return_ref_str.rs create mode 100644 compile_tests/tests/compile-fail/no_unmutable_fnmut.rs create mode 100644 compile_tests/tests/run-pass/do_nothing.rs create mode 100644 compile_tests/tests/tests.rs delete mode 100644 savefile.sublime-project diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f9c5d93..91d9135 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,9 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install nightly - run: rustup toolchain install nightly + run: rustup toolchain install nightly && rustup toolchain install stable + - name: Miri (nightly) + run: rustup component add --toolchain nightly miri && cd savefile-test && cargo +nightly miri test - name: Build (nightly) run: cargo +nightly build --workspace - name: Run tests (nightly) @@ -29,4 +31,7 @@ jobs: - name: Run tests (stable) run: cargo +stable test --workspace - name: Build min-deps (stable) - run: cargo +stable build -p savefile-min-build + run: cargo +stable build -p savefile-min-build + - name: compile_tests (stable) + run: cd compile_tests && cargo +stable test + diff --git a/Cargo.lock b/Cargo.lock index 3840eff..45e1f31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,6 +275,30 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -424,7 +448,7 @@ dependencies = [ [[package]] name = "savefile" -version = "0.17.0-beta.12" +version = "0.17.0-beta.13" dependencies = [ "arrayvec", "bit-set", @@ -447,7 +471,7 @@ dependencies = [ [[package]] name = "savefile-abi" -version = "0.17.0-beta.12" +version = "0.17.0-beta.13" dependencies = [ "byteorder", "libloading", @@ -487,8 +511,9 @@ dependencies = [ [[package]] name = "savefile-derive" -version = "0.17.0-beta.12" +version = "0.17.0-beta.13" dependencies = [ + "proc-macro-error", "proc-macro2", "quote", "syn 1.0.109", @@ -627,6 +652,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 23c2ea6..b56f5b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,13 @@ members = [ "savefile-abi-min", "savefile-abi", "savefile-abi-min-lib", - "savefile-abi-min-lib-impl", + "savefile-abi-min-lib-impl" ] +exclude = ["compile_tests"] + resolver = "2" [profile.dev.package] insta.opt-level = 3 similar.opt-level = 3 + diff --git a/README.md b/README.md index 01bb8bf..7df79b8 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,10 @@ The dataformat for schemas has changed, but in a backward compatible way. Version 0.17.0-beta.11 is, as can be seen from the name, a beta. There will be bugs. +Another thing in 0.17.0: We're starting to use 'release-plz' to manage releases. +Hopefully this will make the releases more professional, with correct git tags, git releases etc. + + ### Upgrade guide from 0.16 -> 0.17 1: Schemas have been expanded. @@ -104,6 +108,10 @@ encode the discriminant. Set to 1 for enums which will never have more than 256 enums. If you ever need an enum to have more than 65536 fields, set it to 4. +## 0.17.0-beta.13 + +Major improvements. Better diagnostics. Support for returning Box. + ## 0.17.0-beta.10 Tentative support for fast serialization/deserialization of enums. May be buggy! diff --git a/compile_tests/Cargo.lock b/compile_tests/Cargo.lock new file mode 100644 index 0000000..3974e67 --- /dev/null +++ b/compile_tests/Cargo.lock @@ -0,0 +1,753 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "compile_tests" +version = "0.1.0" +dependencies = [ + "compiletest_rs", + "savefile", + "savefile-abi", + "savefile-derive", +] + +[[package]] +name = "compiletest_rs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7225fee1bcf9247bb3a1b1a2d7ecfe2f7a990e549a09d766a257a4ae30dac0d6" +dependencies = [ + "diff", + "filetime", + "getopts", + "lazy_static", + "libc", + "log", + "miow", + "regex", + "rustfix", + "serde", + "serde_derive", + "serde_json", + "tempfile", + "tester", + "winapi", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustfix" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481" +dependencies = [ + "anyhow", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "savefile" +version = "0.17.0-beta.13" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "byteorder", + "indexmap", + "memoffset", + "parking_lot", + "rustc_version", + "savefile-derive", + "smallvec", +] + +[[package]] +name = "savefile-abi" +version = "0.17.0-beta.13" +dependencies = [ + "byteorder", + "libloading", + "savefile", + "savefile-derive", +] + +[[package]] +name = "savefile-derive" +version = "0.17.0-beta.13" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.199" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.199" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "serde_json" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "tester" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e8bf7e0eb2dd7b4228cc1b6821fc5114cd6841ae59f652a85488c016091e5f" +dependencies = [ + "cfg-if", + "getopts", + "libc", + "num_cpus", + "term", +] + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/compile_tests/Cargo.toml b/compile_tests/Cargo.toml new file mode 100644 index 0000000..fcd1ff4 --- /dev/null +++ b/compile_tests/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] + +[package] +name = "compile_tests" +version = "0.1.0" +edition = "2021" + +[dependencies] +savefile = { path = "../savefile", features = ["derive"], default-features = false } +savefile-abi = { path = "../savefile-abi" } +savefile-derive = {path = "../savefile-derive"} + + +[dev-dependencies] +compiletest_rs = { version = "0.10", features = [ "tmp" ] } + +[features] +rustc = ["compiletest_rs/rustc"] +assembly = [] diff --git a/compile_tests/src/main.rs b/compile_tests/src/main.rs new file mode 100644 index 0000000..0e7fb47 --- /dev/null +++ b/compile_tests/src/main.rs @@ -0,0 +1,3 @@ + +pub fn main() { +} diff --git a/compile_tests/tests/compile-fail/abi_ref_return.rs b/compile_tests/tests/compile-fail/abi_ref_return.rs new file mode 100644 index 0000000..8ca8d76 --- /dev/null +++ b/compile_tests/tests/compile-fail/abi_ref_return.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn set(&mut self, x: u32) -> &u32; +//~^ 14:34: 14:35: Method 'set': savefile-abi does not support methods returning references. +} + +fn main() {} \ No newline at end of file diff --git a/compile_tests/tests/compile-fail/method_with_lifetimes.rs b/compile_tests/tests/compile-fail/method_with_lifetimes.rs new file mode 100644 index 0000000..23a211e --- /dev/null +++ b/compile_tests/tests/compile-fail/method_with_lifetimes.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn global_func<'a>(&self, x: &'a u32) -> u32; +//~^ 14:20: 14:22: savefile-abi does not support methods with lifetimes. +} + +fn main() {} diff --git a/compile_tests/tests/compile-fail/missing_self.rs b/compile_tests/tests/compile-fail/missing_self.rs new file mode 100644 index 0000000..981ccf9 --- /dev/null +++ b/compile_tests/tests/compile-fail/missing_self.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn global_func(x: u32) -> u32; +//~^ 14:5: 14:7: Method 'global_func' must have 'self'-parameter (savefile-abi does not support methods without self) +} + +fn main() {} \ No newline at end of file diff --git a/compile_tests/tests/compile-fail/no_impl_trait.rs b/compile_tests/tests/compile-fail/no_impl_trait.rs new file mode 100644 index 0000000..ca4cd63 --- /dev/null +++ b/compile_tests/tests/compile-fail/no_impl_trait.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn example_func(&self, x: impl Fn()); +//~^ 14:31: 14:35: Method 'example_func', argument x, type is unsupported by savefile-abi: impl Fn() +} + +fn main() {} diff --git a/compile_tests/tests/compile-fail/no_mut_data_ref.rs b/compile_tests/tests/compile-fail/no_mut_data_ref.rs new file mode 100644 index 0000000..97cad13 --- /dev/null +++ b/compile_tests/tests/compile-fail/no_mut_data_ref.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn example_func(&self, x: &mut u32); +//~^ 14:36: 14:39: Method 'example_func', argument x: Mutable references are not supported by savefile-abi (except for trait objects) +} + +fn main() {} diff --git a/compile_tests/tests/compile-fail/no_mutable_fn.rs b/compile_tests/tests/compile-fail/no_mutable_fn.rs new file mode 100644 index 0000000..07ab80d --- /dev/null +++ b/compile_tests/tests/compile-fail/no_mutable_fn.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn example_func(&self, x: &mut dyn Fn()); +//~^ 14:40: 14:42: Method 'example_func', argument x: Mutable references to Fn are not supported by savefile-abi. Try using a non-mutable reference instead. +} + +fn main() {} diff --git a/compile_tests/tests/compile-fail/no_ref_box.rs b/compile_tests/tests/compile-fail/no_ref_box.rs new file mode 100644 index 0000000..030e844 --- /dev/null +++ b/compile_tests/tests/compile-fail/no_ref_box.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn example_func(&self, x: &Box); +//~^ 14:32: 14:35: Savefile does not support reference to Box. This is also generally not very useful, just use a regular reference for arguments. +} + +fn main() {} diff --git a/compile_tests/tests/compile-fail/no_ref_ref.rs b/compile_tests/tests/compile-fail/no_ref_ref.rs new file mode 100644 index 0000000..3900eff --- /dev/null +++ b/compile_tests/tests/compile-fail/no_ref_ref.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn example_func(&self, x: &&u32); +//~^ 14:32: 14:33: Method 'example_func', argument x: Method arguments cannot be reference to reference in savefile-abi. Try removing a '&' from the type: & u32 +} + +fn main() {} diff --git a/compile_tests/tests/compile-fail/no_return_ref_str.rs b/compile_tests/tests/compile-fail/no_return_ref_str.rs new file mode 100644 index 0000000..cf8bc4d --- /dev/null +++ b/compile_tests/tests/compile-fail/no_return_ref_str.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn get_str(&self) -> &str; +//~^ 14:26: 14:27: Method 'get_str': savefile-abi does not support methods returning &str. Use "String" or "&'static str" instead +} + +fn main() {} \ No newline at end of file diff --git a/compile_tests/tests/compile-fail/no_unmutable_fnmut.rs b/compile_tests/tests/compile-fail/no_unmutable_fnmut.rs new file mode 100644 index 0000000..f1e5347 --- /dev/null +++ b/compile_tests/tests/compile-fail/no_unmutable_fnmut.rs @@ -0,0 +1,18 @@ +extern crate savefile; +extern crate savefile_abi; +extern crate savefile_derive; +use std::collections::HashMap; +use savefile::prelude::*; +use savefile::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Debug; +use std::io::{BufWriter, Cursor, Write}; +use savefile_abi::AbiConnection; +use savefile_derive::savefile_abi_exportable; + +#[savefile_abi_exportable(version = 0)] +pub trait ExampleTrait { + fn example_func(&self, x: &dyn FnMut()); +//~^ 14:36: 14:41: Method 'example_func', argument x: When using FnMut, it must be referenced using &mut, not &. Otherwise, it is impossible to call. +} + +fn main() {} diff --git a/compile_tests/tests/run-pass/do_nothing.rs b/compile_tests/tests/run-pass/do_nothing.rs new file mode 100644 index 0000000..ea3850b --- /dev/null +++ b/compile_tests/tests/run-pass/do_nothing.rs @@ -0,0 +1,2 @@ +fn main() { +} \ No newline at end of file diff --git a/compile_tests/tests/tests.rs b/compile_tests/tests/tests.rs new file mode 100644 index 0000000..0ba7059 --- /dev/null +++ b/compile_tests/tests/tests.rs @@ -0,0 +1,31 @@ +extern crate compiletest_rs as compiletest; + +use std::env; +use std::path::PathBuf; + +fn run_mode(mode: &'static str, custom_dir: Option<&'static str>) { + let mut config = compiletest::Config::default().tempdir(); + let cfg_mode = mode.parse().expect("Invalid mode"); + + config.mode = cfg_mode; + + let dir = custom_dir.unwrap_or(mode); + config.src_base = PathBuf::from(format!("tests/{}", dir)); + config.target_rustcflags = Some("-L target/debug -L target/debug/deps".to_string()); + config.llvm_filecheck = Some( + env::var("FILECHECK") + .unwrap_or("FileCheck".to_string()) + .into(), + ); + //config.clean_rmeta(); + //config.clean_rlib(); + config.strict_headers = true; + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail", None); + run_mode("run-pass", None); +} diff --git a/release-plz.toml b/release-plz.toml index bca1a2f..76b5f83 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -1,4 +1,5 @@ [workspace] +git_release_enable = false [[package]] name= "savefile" @@ -18,6 +19,11 @@ publish = true release = true +[[package]] +name= "compile_tests" +publish = false +release = false + [[package]] name= "savefile-test" publish = false diff --git a/savefile-abi-min-lib-impl/src/lib.rs b/savefile-abi-min-lib-impl/src/lib.rs index c8ed483..3c3d874 100644 --- a/savefile-abi-min-lib-impl/src/lib.rs +++ b/savefile-abi-min-lib-impl/src/lib.rs @@ -35,7 +35,6 @@ impl AdderInterface for AdderImplementation { x + y } - fn do_nothing(&self) { - } + fn do_nothing(&self) {} } savefile_abi_export!(AdderImplementation, AdderInterface); diff --git a/savefile-abi-min/src/main.rs b/savefile-abi-min/src/main.rs index d75fc5d..8d19b8b 100644 --- a/savefile-abi-min/src/main.rs +++ b/savefile-abi-min/src/main.rs @@ -27,7 +27,7 @@ pub fn call_add(adder: &AbiConnection, a: u32, b: u32) -> u3 adder.add_simple(a, b) } #[no_mangle] -pub extern fn call_do_nothing(adder: &AbiConnection) { +pub extern "C" fn call_do_nothing(adder: &AbiConnection) { adder.do_nothing(); } diff --git a/savefile-abi/CHANGELOG.md b/savefile-abi/CHANGELOG.md index 9c31f18..34c17e4 100644 --- a/savefile-abi/CHANGELOG.md +++ b/savefile-abi/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.17.-beta.13](https://github.com/avl/savefile/compare/savefile-abi-v0.17.0-beta.12...savefile-abi-v0.17.0-beta.13) - 2024-04-27 + +### Other +- updated the following local packages: savefile + ## [0.17.0-beta.12](https://github.com/avl/savefile/compare/savefile-abi-v0.17.0-beta.11...savefile-abi-v0.17.0-beta.12) - 2024-04-27 ### Other diff --git a/savefile-abi/Cargo.toml b/savefile-abi/Cargo.toml index 3a03f2a..21156da 100644 --- a/savefile-abi/Cargo.toml +++ b/savefile-abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "savefile-abi" -version = "0.17.0-beta.12" +version = "0.17.0-beta.13" edition = "2021" authors = ["Anders Musikka "] documentation = "https://docs.rs/savefile-abi/" @@ -16,7 +16,7 @@ keywords = ["dylib", "dlopen", "ffi"] license = "MIT/Apache-2.0" [dependencies] -savefile = { path="../savefile", version = "=0.17.0-beta.12" } -savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.12" } +savefile = { path="../savefile", version = "=0.17.0-beta.13" } +savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.13" } byteorder = "1.4" libloading = "0.8" diff --git a/savefile-abi/src/lib.rs b/savefile-abi/src/lib.rs index c95463a..7d6a7cb 100644 --- a/savefile-abi/src/lib.rs +++ b/savefile-abi/src/lib.rs @@ -468,6 +468,13 @@ pub struct TraitObject { vtable: *const (), } +unsafe impl Sync for TraitObject { + +} +unsafe impl Send for TraitObject { + +} + impl TraitObject { /// Returns a TraitObject with two null ptrs. This value must never be used, /// but can serve as a default before the real value is written. @@ -610,6 +617,12 @@ pub struct AbiConnection { #[doc(hidden)] pub phantom: PhantomData<*const T>, } +unsafe impl Sync for AbiConnection{ + +} +unsafe impl Send for AbiConnection{ + +} /// A trait object together with its entry point #[repr(C)] @@ -810,7 +823,10 @@ pub enum AbiProtocol { } /// Parse the given RawAbiCallResult. If it concerns a success, then deserialize a return value using the given closure. -pub fn parse_return_value_impl(outcome: &RawAbiCallResult, deserialize_action: impl FnOnce(&mut Deserializer>) -> Result ) -> Result { +pub fn parse_return_value_impl( + outcome: &RawAbiCallResult, + deserialize_action: impl FnOnce(&mut Deserializer>) -> Result, +) -> Result { match outcome { RawAbiCallResult::Success { data, len } => { let data = unsafe { std::slice::from_raw_parts(*data, *len) }; @@ -842,10 +858,18 @@ pub fn parse_return_value_impl(outcome: &RawAbiCallResult, deserialize_action /// Parse an RawAbiCallResult instance into a Result, SavefileError> . /// This is used on the caller side, and the type T will always be statically known. /// TODO: There's some duplicated code here, compare parse_return_value -pub fn parse_return_boxed_trait(outcome: &RawAbiCallResult) -> Result>, SavefileError> where T : AbiExportable + ?Sized { - parse_return_value_impl(outcome, |deserializer|{ - let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? }; - unsafe { Ok(Box::new(AbiConnection::::from_raw_packaged(packaged, Owning::Owned)?)) } +pub fn parse_return_boxed_trait(outcome: &RawAbiCallResult) -> Result>, SavefileError> +where + T: AbiExportable + ?Sized, +{ + parse_return_value_impl(outcome, |deserializer| { + let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? }; + unsafe { + Ok(Box::new(AbiConnection::::from_raw_packaged( + packaged, + Owning::Owned, + )?)) + } }) } /// We never unload libraries which have been dynamically loaded, because of all the problems with @@ -946,30 +970,31 @@ pub unsafe extern "C" fn abi_result_receiver( ) { let outcome = unsafe { &*outcome }; let result_receiver = unsafe { &mut *(result_receiver as *mut std::mem::MaybeUninit>) }; - result_receiver.write( - parse_return_value_impl(outcome, |deserializer|{ - T::deserialize(deserializer) - }) - ); + result_receiver.write(parse_return_value_impl(outcome, |deserializer| { + T::deserialize(deserializer) + })); } /// Raw entry point for receiving return values from other shared libraries #[doc(hidden)] -pub unsafe extern "C" fn abi_boxed_trait_receiver( - outcome: *const RawAbiCallResult, - result_receiver: *mut (), -) where T: AbiExportable + ?Sized { +pub unsafe extern "C" fn abi_boxed_trait_receiver(outcome: *const RawAbiCallResult, result_receiver: *mut ()) +where + T: AbiExportable + ?Sized, +{ let outcome = unsafe { &*outcome }; - let result_receiver = unsafe { &mut *(result_receiver as *mut std::mem::MaybeUninit>, SavefileError>>) }; - result_receiver.write( - parse_return_value_impl(outcome, |deserializer|{ - let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? }; - unsafe { Ok(Box::new(AbiConnection::::from_raw_packaged(packaged, Owning::Owned)?)) } - }) - ); + let result_receiver = + unsafe { &mut *(result_receiver as *mut std::mem::MaybeUninit>, SavefileError>>) }; + result_receiver.write(parse_return_value_impl(outcome, |deserializer| { + let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? }; + unsafe { + Ok(Box::new(AbiConnection::::from_raw_packaged( + packaged, + Owning::Owned, + )?)) + } + })); } - // Flex buffer is only used internally, and we don't need to provide // any of the regular convenience. #[allow(clippy::new_without_default)] @@ -1025,20 +1050,25 @@ fn arg_layout_compatible( .is_ok() } (Schema::Boxed(native_a), Schema::Boxed(native_b)) => { - let (Schema::Boxed(effective_a2), Schema::Boxed(effective_b2)) = (a_effective, b_effective) - else { + let (Schema::Boxed(effective_a2), Schema::Boxed(effective_b2)) = (a_effective, b_effective) else { return false; }; - arg_layout_compatible(&**native_a, &**native_b, &**effective_a2, &**effective_b2, effective_version) + arg_layout_compatible( + &**native_a, + &**native_b, + &**effective_a2, + &**effective_b2, + effective_version, + ) } (Schema::Trait(s_a, _), Schema::Trait(s_b, _)) => { if s_a != s_b { return false; } let (Schema::Trait(e_a2, effective_a2), Schema::Trait(e_b2, effective_b2)) = (a_effective, b_effective) - else { - return false; - }; + else { + return false; + }; if e_a2 != e_b2 { return false; } @@ -1069,7 +1099,6 @@ impl AbiConnection { panic!("Too many method arguments, max 64 are supported!"); } for caller_native_method in caller_native_definition.methods.into_iter() { - println!("Checking {}", caller_native_method.name); let Some((callee_native_method_number, callee_native_method)) = callee_native_definition .methods .iter() @@ -1145,38 +1174,28 @@ impl AbiConnection { }); } let mut mask = 0; - let mut check_diff = |effective1,effective2,native1,native2,index:Option|{ - - let effective_schema_diff = diff_schema( - effective1, - effective2, - "".to_string(), - ); + let mut check_diff = |effective1, effective2, native1, native2, index: Option| { + let effective_schema_diff = diff_schema(effective1, effective2, "".to_string()); if let Some(diff) = effective_schema_diff { return Err(SavefileError::IncompatibleSchema { - message: - if let Some(index) = index { - format!( - "Incompatible ABI detected. Trait: {}, method: {}, argument: #{}: {}", - trait_name, &caller_native_method.name, index, diff) - } else { - format!( - "Incompatible ABI detected. Trait: {}, method: {}, return value differs: {}", - trait_name, &caller_native_method.name, diff) - } + message: if let Some(index) = index { + format!( + "Incompatible ABI detected. Trait: {}, method: {}, argument: #{}: {}", + trait_name, &caller_native_method.name, index, diff + ) + } else { + format!( + "Incompatible ABI detected. Trait: {}, method: {}, return value differs: {}", + trait_name, &caller_native_method.name, diff + ) + }, }); } //let caller_isref = caller_native_method.info.arguments[index].can_be_sent_as_ref; //let callee_isref = callee_native_method.info.arguments[index].can_be_sent_as_ref; - let comp = arg_layout_compatible( - native1, - native2, - effective1, - effective2, - effective_version, - ); + let comp = arg_layout_compatible(native1, native2, effective1, effective2, effective_version); if comp { if let Some(index) = index { @@ -1186,14 +1205,12 @@ impl AbiConnection { Ok(()) }; - for index in 0..caller_native_method.info.arguments.len() { - let effective1 = &caller_effective_method.info.arguments[index].schema; let effective2 = &callee_effective_method.info.arguments[index].schema; let native1 = &caller_native_method.info.arguments[index].schema; let native2 = &callee_native_method.info.arguments[index].schema; - check_diff(effective1,effective2,native1,native2, Some(index))?; + check_diff(effective1, effective2, native1, native2, Some(index))?; } check_diff( @@ -1201,8 +1218,8 @@ impl AbiConnection { &callee_effective_method.info.return_value, &caller_native_method.info.return_value, &callee_native_method.info.return_value, - None /*return value*/)?; - + None, /*return value*/ + )?; methods.push(AbiConnectionMethod { method_name: caller_native_method.name, @@ -1481,7 +1498,6 @@ impl AbiConnection { /// # Safety /// The 'AbiProtocol' protocol must only contain valid data. pub unsafe fn abi_entry_light(flag: AbiProtocol) { - match flag { AbiProtocol::RegularCall { trait_object, @@ -1521,7 +1537,7 @@ pub unsafe fn abi_entry_light(flag: AbiProtocol) { match result { Ok(()) => {} Err(err) => { - let msg:&str; + let msg: &str; let temp; if let Some(err) = err.downcast_ref::<&str>() { msg = err; @@ -1619,7 +1635,7 @@ pub unsafe fn abi_entry(flag: AbiProtocol) { match result { Ok(_) => {} Err(err) => { - let msg:&str; + let msg: &str; let temp; if let Some(err) = err.downcast_ref::<&str>() { msg = err; diff --git a/savefile-derive/CHANGELOG.md b/savefile-derive/CHANGELOG.md index 8a0abb2..92047a1 100644 --- a/savefile-derive/CHANGELOG.md +++ b/savefile-derive/CHANGELOG.md @@ -9,11 +9,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.17.0-beta.12](https://github.com/avl/savefile/compare/savefile-derive-v0.17.0-beta.11...savefile-derive-v0.17.0-beta.12) - 2024-04-27 ### Other -- wip -- miri passes -- wip -- wip -- wip -- Better support for trait objects -- Work on safety -- Fix formatting +These CHANGELOG-files are experimental. \ No newline at end of file diff --git a/savefile-derive/Cargo.toml b/savefile-derive/Cargo.toml index 403c405..e06843c 100644 --- a/savefile-derive/Cargo.toml +++ b/savefile-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "savefile-derive" -version = "0.17.0-beta.12" +version = "0.17.0-beta.13" authors = ["Anders Musikka "] description = "Custom derive macros for savefile crate - simple, convenient, fast, versioned, binary serialization/deserialization library." @@ -24,5 +24,5 @@ proc-macro = true quote = "1.0" syn = { version = "1.0" , features = ["full","extra-traits"]} proc-macro2 = { version = "1.0", features = ["nightly"] } - +proc-macro-error = "1.0" diff --git a/savefile-derive/src/common.rs b/savefile-derive/src/common.rs index de35da9..7580f6d 100644 --- a/savefile-derive/src/common.rs +++ b/savefile-derive/src/common.rs @@ -1,5 +1,6 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::ToTokens; +use syn::spanned::Spanned; use syn::{Expr, GenericParam, Generics, Lit, Type, WhereClause}; pub(crate) fn get_extra_where_clauses( @@ -152,7 +153,7 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { let default_fn_str_lit = match &x.lit { &syn::Lit::Str(ref litstr) => litstr, _ => { - panic!("Unexpected attribute value, please specify savefile_default_fn method names within quotes."); + abort!(x.lit.span(), "Unexpected attribute value, please specify savefile_default_fn method names within quotes."); } }; default_fn = Some(syn::Ident::new( @@ -173,7 +174,7 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { let output2: Vec = litstr2.value().splitn(3, ':').map(|x| x.to_string()).collect(); if output2.len() != 3 && output2.len() != 2 { - panic!("The #savefile_versions_as tag must contain a version range and a deserialization type, such as : #[savefile_versions_as=0..3:MyStructType]"); + abort!(litstr2.span(), "The #savefile_versions_as tag must contain a version range and a deserialization type, such as : #[savefile_versions_as=0..3:MyStructType]"); } let litstr = &output2[0]; @@ -190,7 +191,7 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { let output: Vec = litstr.split("..").map(|x| x.to_string()).collect(); if output.len() != 2 { - panic!("savefile_versions_as tag must contain a (possibly half-open) range, such as 0..3 or 2.. (fields present in all versions to date should not use the savefile_versions_as-attribute)"); + abort!(litstr2.span(), "savefile_versions_as tag must contain a (possibly half-open) range, such as 0..3 or 2.. (fields present in all versions to date should not use the savefile_versions_as-attribute)"); } let (a, b) = (output[0].to_string(), output[1].to_string()); @@ -199,7 +200,7 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { } else if let Ok(a_u32) = a.parse::() { a_u32 } else { - panic!("The from version in the version tag must be an integer. Use #[savefile_versions_as=0..3:MyStructType] for example"); + abort!(litstr2.span(), "The from version in the version tag must be an integer. Use #[savefile_versions_as=0..3:MyStructType] for example"); }; let to_ver = if b.trim() == "" { @@ -207,10 +208,10 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { } else if let Ok(b_u32) = b.parse::() { b_u32 } else { - panic!("The to version in the version tag must be an integer. Use #[savefile_versions_as=0..3:MyStructType] for example"); + abort!(litstr2.span(), "The to version in the version tag must be an integer. Use #[savefile_versions_as=0..3:MyStructType] for example"); }; if to_ver < from_ver { - panic!("Version ranges must specify lower number first."); + abort!(litstr2.span(), "Version ranges must specify lower number first."); } let item = VersionRange { @@ -220,11 +221,17 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { serialized_type: version_type.to_string(), }; if deser_types.iter().any(overlap(&item)) { - panic!("#savefile_versions_as attributes may not specify overlapping ranges"); + abort!( + litstr2.span(), + "#savefile_versions_as attributes may not specify overlapping ranges" + ); } deser_types.push(item); } - _ => panic!("Unexpected datatype for value of attribute savefile_versions_as"), + _ => abort!( + x.path.span(), + "Unexpected datatype for value of attribute savefile_versions_as" + ), } } @@ -233,19 +240,22 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { &syn::Lit::Str(ref litstr) => { let output: Vec = litstr.value().split("..").map(|x| x.to_string()).collect(); if output.len() != 2 { - panic!("savefile_versions tag must contain a (possibly half-open) range, such as 0..3 or 2.. (fields present in all versions to date should not use the savefile_versions-attribute)"); + abort!(litstr.span(), "savefile_versions tag must contain a (possibly half-open) range, such as 0..3 or 2.. (fields present in all versions to date should not use the savefile_versions-attribute)"); } let (a, b) = (output[0].to_string(), output[1].to_string()); if field_from_version.is_some() || field_to_version.is_some() { - panic!("There can only be one savefile_versions attribute on each field.") + abort!( + litstr.span(), + "There can only be one savefile_versions attribute on each field." + ) } if a.trim() == "" { field_from_version = Some(0); } else if let Ok(a_u32) = a.parse::() { field_from_version = Some(a_u32); } else { - panic!("The from version in the version tag must be an integer. Use #[savefile_versions=0..3] for example"); + abort!(litstr.span(), "The from version in the version tag must be an integer. Use #[savefile_versions=0..3] for example"); } if b.trim() == "" { @@ -253,21 +263,27 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { } else if let Ok(b_u32) = b.parse::() { field_to_version = Some(b_u32); } else { - panic!("The to version in the version tag must be an integer. Use #[savefile_versions=0..3] for example"); + abort!(litstr.span(), "The to version in the version tag must be an integer. Use #[savefile_versions=0..3] for example"); } if field_to_version.expect("Expected field_to_version") < field_from_version.expect("expected field_from_version") { - panic!("savefile_versions ranges must specify lower number first."); + abort!( + litstr.span(), + "savefile_versions ranges must specify lower number first." + ); } } - _ => panic!("Unexpected datatype for value of attribute savefile_versions"), + _ => abort!( + x.lit.span(), + "Unexpected datatype for value of attribute savefile_versions" + ), } } } }, Err(e) => { - panic!("Unparsable attribute: {:?} ({:?})", e, attr.tokens); + abort!(attr.span(), "Unparsable attribute: {:?} ({:?})", e, attr.tokens); } } } @@ -279,11 +295,11 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { serialized_type: "dummy".to_string(), }; if deser_types.iter().any(overlap(&versions_tag_range)) { - panic!("The version ranges of #version_as attributes may not overlap those of #savefile_versions"); + abort_call_site!("The version ranges of #version_as attributes may not overlap those of #savefile_versions"); } for dt in deser_types.iter() { if dt.to >= field_from_version.unwrap_or(0) { - panic!("The version ranges of #version_as attributes must be lower than those of the #savefile_versions attribute."); + abort!(dt.to.span(), "The version ranges of #version_as attributes must be lower than those of the #savefile_versions attribute."); } } @@ -300,6 +316,7 @@ pub(crate) fn parse_attr_tag(attrs: &[syn::Attribute]) -> AttrsResult { } pub(crate) struct FieldInfo<'a> { + pub(crate) field_span: Span, pub(crate) ident: Option, pub(crate) index: u32, pub(crate) ty: &'a syn::Type, diff --git a/savefile-derive/src/deserialize.rs b/savefile-derive/src/deserialize.rs index e0a5c09..173fb33 100644 --- a/savefile-derive/src/deserialize.rs +++ b/savefile-derive/src/deserialize.rs @@ -1,6 +1,7 @@ use common::{check_is_remove, get_extra_where_clauses, parse_attr_tag, FieldInfo, RemovedType}; use get_enum_size; use proc_macro2::{Literal, TokenStream}; +use syn::spanned::Spanned; use syn::DeriveInput; fn implement_deserialize(field_infos: Vec) -> Vec { @@ -52,12 +53,18 @@ fn implement_deserialize(field_infos: Vec) -> Vec { quote_spanned! { span => Default::default() } }; if field_from_version > field_to_version { - panic!("Version range is reversed. This is not allowed. Version must be range like 0..2, not like 2..0"); + abort!( + field.field_span, + "Version range is reversed. This is not allowed. Version must be range like 0..2, not like 2..0" + ); } let src = if field_from_version == 0 && field_to_version == std::u32::MAX && !verinfo.ignore { if is_removed.is_removed() { - panic!("The Removed type may only be used for fields which have an old version."); + abort!( + field_type.span(), + "The Removed type may only be used for fields which have an old version." + ); //TODO: Better message, tell user how to do this annotation }; quote_spanned! { span => @@ -161,6 +168,7 @@ pub fn savefile_derive_crate_deserialize(input: DeriveInput) -> TokenStream { .enumerate() .map(|(field_index, field)| FieldInfo { ident: Some(field.ident.clone().expect("Expected identifier [6]")), + field_span: field.ident.as_ref().unwrap().span(), ty: &field.ty, index: field_index as u32, attrs: &field.attrs, @@ -178,6 +186,7 @@ pub fn savefile_derive_crate_deserialize(input: DeriveInput) -> TokenStream { .enumerate() .map(|(field_index, field)| FieldInfo { ident: None, + field_span: field.ty.span(), ty: &field.ty, index: field_index as u32, attrs: &field.attrs, @@ -229,6 +238,7 @@ pub fn savefile_derive_crate_deserialize(input: DeriveInput) -> TokenStream { .enumerate() .map(|(field_index, field)| FieldInfo { ident: Some(field.ident.clone().expect("Expected identifier[7]")), + field_span: field.ident.as_ref().unwrap().span(), index: field_index as u32, ty: &field.ty, attrs: &field.attrs, @@ -247,6 +257,7 @@ pub fn savefile_derive_crate_deserialize(input: DeriveInput) -> TokenStream { .enumerate() .map(|(field_index, field)| FieldInfo { ident: None, + field_span: field.ty.span(), index: field_index as u32, ty: &field.ty, attrs: &field.attrs, @@ -279,7 +290,7 @@ pub fn savefile_derive_crate_deserialize(input: DeriveInput) -> TokenStream { } } _ => { - panic!("Only regular structs are supported"); + abort_call_site!("Only regular structs are supported"); } }; diff --git a/savefile-derive/src/lib.rs b/savefile-derive/src/lib.rs index 9da9b53..d52dcab 100644 --- a/savefile-derive/src/lib.rs +++ b/savefile-derive/src/lib.rs @@ -19,6 +19,8 @@ extern crate proc_macro2; #[macro_use] extern crate quote; extern crate syn; +#[macro_use] +extern crate proc_macro_error; use common::{ check_is_remove, compile_time_check_reprc, compile_time_size, get_extra_where_clauses, parse_attr_tag, @@ -31,13 +33,10 @@ use std::collections::{HashMap, HashSet}; #[allow(unused_imports)] use std::iter::IntoIterator; use syn::__private::bool; +use syn::spanned::Spanned; use syn::token::Paren; use syn::Type::Tuple; -use syn::{ - DeriveInput, FnArg, Generics, Ident, ImplGenerics, Index, ItemTrait, Pat, ReturnType, TraitItem, Type, - TypeGenerics, TypeTuple, -}; - +use syn::{DeriveInput, FnArg, GenericParam, Generics, Ident, ImplGenerics, Index, ItemTrait, Pat, ReturnType, TraitItem, Type, TypeGenerics, TypeParamBound, TypeTuple}; fn implement_fields_serialize( field_infos: Vec, implicit_self: bool, @@ -139,7 +138,8 @@ fn implement_fields_serialize( if field_from_version == 0 && field_to_version == std::u32::MAX { if removed.is_removed() { - panic!( + abort!( + field.ty.span(), "The Removed type can only be used for removed fields. Use the savefile_versions attribute." ); } @@ -212,6 +212,7 @@ mod deserialize; mod savefile_abi; +#[proc_macro_error] #[proc_macro_attribute] pub fn savefile_abi_exportable( attr: proc_macro::TokenStream, @@ -223,7 +224,8 @@ pub fn savefile_abi_exportable( for item in attr.to_string().split(',') { let keyvals: Vec<_> = item.split('=').collect(); if keyvals.len() != 2 { - panic!( + abort!( + item.span(), "savefile_abi_exportable arguments should be of form #[savefile_abi_exportable(version=0)], not '{}'", attr ); @@ -233,14 +235,14 @@ pub fn savefile_abi_exportable( match key { "version" => { if version.is_some() { - panic!("version specified more than once"); + abort!(item.span(), "version specified more than once"); } version = Some( val.parse() - .unwrap_or_else(|_| panic!("Version must be numeric, but was: {}", val)), + .unwrap_or_else(|_| abort!(item.span(), "Version must be numeric, but was: {}", val)), ); } - _ => panic!("Unknown savefile_abi_exportable key: '{}'", key), + _ => abort!(item.span(), "Unknown savefile_abi_exportable key: '{}'", key), } } let version: u32 = version.unwrap_or(0); @@ -263,20 +265,55 @@ pub fn savefile_abi_exportable( let mut caller_method_trampoline = vec![]; let mut extra_definitions = HashMap::new(); + if parsed.generics.params.is_empty() == false { + abort!(parsed.generics.params.span(), "Savefile does not support generic traits."); + } + for supertrait in parsed.supertraits.iter() { + match supertrait { + TypeParamBound::Trait(trait_bound) => { + if let Some(lif) = &trait_bound.lifetimes { + abort!(lif.span(), "Savefile does not support lifetimes"); + } + if let Some(seg) = trait_bound.path.segments.last() { + let id = seg.ident.to_string(); + match id.as_str() { + "Copy" => abort!(seg.span(), "Savefile does not support Copy bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper can't be copy."), + "Clone" => abort!(seg.span(), "Savefile does not support Clone bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper can't be clone."), + "Sync"|"Send"|"Debug" => {/* these are ok, the wrappers actually do implement these*/} + _ => abort!(seg.span(), "Savefile does not support bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper can't fulfill implement arbitrary bounds."), + } + } + } + TypeParamBound::Lifetime(lif) => { + if lif.ident != "static" { + abort!(lif.span(), "Savefile does not support lifetimes"); + } + } + } + } + + if parsed.generics.where_clause.is_some() { + abort!(parsed.generics.where_clause.span(), "Savefile does not support where-clauses for traits"); + } + for (method_number, item) in parsed.items.iter().enumerate() { if method_number > u16::MAX.into() { - panic!("Savefile only supports 2^16 methods per interface. Sorry."); + abort!(item.span(), "Savefile only supports 2^16 methods per interface. Sorry."); } let method_number = method_number as u16; match item { TraitItem::Const(c) => { - panic!( + abort!( + c.span(), "savefile_abi_exportable does not support associated consts: {}", c.ident ); } TraitItem::Method(method) => { + if method.sig.generics.where_clause.is_some() { + abort!(method.sig.generics.where_clause.span(), "Savefile does not support where-clauses for methods"); + } let method_name = method.sig.ident.clone(); //let method_name_str = method.sig.ident.to_string(); //let mut metadata_arguments = vec![]; @@ -308,16 +345,18 @@ pub fn savefile_abi_exportable( } let self_arg = method.sig.inputs.iter().next().unwrap_or_else(|| { - panic!( - "Method {} has no arguments. This is not supported - it must at least have a self-argument.", + abort!( + method.span(), + "Method '{}' has no arguments. This is not supported by savefile-abi - it must at least have a self-argument.", method_name ) }); if let FnArg::Receiver(recv) = self_arg { if let Some(reference) = &recv.reference { if reference.1.is_some() { - panic!( - "Method {} has a lifetime for 'self' argument. This is not supported", + abort!( + reference.1.as_ref().unwrap().span(), + "Method '{}' has a lifetime for 'self' argument. This is not supported by savefile-abi", method_name ); } @@ -325,29 +364,55 @@ pub fn savefile_abi_exportable( receiver_is_mut = true; } } else { - panic!( - "Method {} takes 'self' by value. This is not supported. Use &self", + abort!( + self_arg.span(), + "Method '{}' takes 'self' by value. This is not supported by savefile-abi. Use &self", method_name ); } } else { - panic!("Method {} must have 'self'-parameter", method_name); + abort!( + method.sig.span(), + "Method '{}' must have 'self'-parameter (savefile-abi does not support methods without self)", + method_name + ); } let mut args = Vec::with_capacity(method.sig.inputs.len()); - for (arg_index, arg) in method.sig.inputs.iter().enumerate().skip(1) { + for arg in method.sig.inputs.iter().skip(1) { match arg { FnArg::Typed(typ) => { match &*typ.pat { Pat::Ident(name) => { args.push((name.ident.clone(), &*typ.ty)); } - _ => panic!("Method {} had a parameter (#{}, where self is #0) which contained a complex pattern. This is not supported.", method_name, arg_index) + _ => abort!(typ.pat.span(), "Method '{}' has a parameter which contains a complex pattern. This is not supported by savefile-abi.", method_name) } }, - _ => panic!("Unexpected error: method {} had a self parameter that wasn't the first parameter!", method_name) + _ => abort!(arg.span(), "Unexpected error: method {} had a self parameter that wasn't the first parameter!", method_name) } } - + if method.sig.asyncness.is_some() { + abort!(method.sig.asyncness.span(), "savefile-abi does not support async methods.") + } + if method.sig.variadic.is_some() { + abort!(method.sig.variadic.span(), "savefile-abi does not support variadic methods.") + } + if method.sig.unsafety.is_some() { + abort!(method.sig.unsafety.span(), "savefile-abi does not presently support unsafe methods.") + } + if method.sig.abi.is_some() { + abort!(method.sig.abi.span(), "savefile-abi does not need (or support) 'extern \"C\"' or similar ABI-constructs. Just remove this keyword.") + } + if method.sig.generics.params.is_empty() == false { + for item in method.sig.generics.params.iter() { + match item { + GenericParam::Type(typ) => abort!(typ.span(), "savefile-abi does not support generic methods."), + GenericParam::Const(typ) => abort!(typ.span(), "savefile-abi does not support const-generic methods."), + _ => {} + } + } + abort!(method.sig.generics.params.span(), "savefile-abi does not support methods with lifetimes."); + } let method_defs = crate::savefile_abi::generate_method_definitions( version, @@ -367,12 +432,23 @@ pub fn savefile_abi_exportable( caller_method_trampoline.push(method_defs.caller_method_trampoline); } TraitItem::Type(t) => { - panic!("savefile_abi_exportable does not support associated types: {}", t.ident); + abort!( + t.span(), + "savefile_abi_exportable does not support associated types: {}", + t.ident + ); } TraitItem::Macro(m) => { - panic!("savefile_abi_exportable does not support macro items: {:?}", m); + abort!( + m.span(), + "savefile_abi_exportable does not support macro items: {:?}", + m + ); } - x => panic!("Unsupported item in trait definition: {:?}", x), + TraitItem::Verbatim(v) => { + abort!(v.span(), "Unsupported item in trait definition: {}", v.to_token_stream()); + } + x => abort!(x.span(), "Unsupported item in trait definition: {}", x.to_token_stream()), } } @@ -426,7 +502,7 @@ pub fn savefile_abi_exportable( //let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site()); let input = TokenStream::from(input); - let extra_definitions:Vec<_> = extra_definitions.values().map(|(_,x)|x).collect(); + let extra_definitions: Vec<_> = extra_definitions.values().map(|(_, x)| x).collect(); let expanded = quote! { #[allow(clippy::double_comparisons)] #[allow(clippy::needless_late_init)] @@ -450,12 +526,13 @@ pub fn savefile_abi_exportable( expanded.into() } +#[proc_macro_error] #[proc_macro] pub fn savefile_abi_export(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = item.to_string(); let symbols: Vec<_> = input.split(',').map(|x| x.trim()).collect(); if symbols.len() != 2 { - panic!("savefile_abi_export requires two parameters. The first parameter is the implementing type, the second is the trait it implements."); + abort!(input.span(), "savefile_abi_export requires two parameters. The first parameter is the implementing type, the second is the trait it implements."); } let defspan = Span::call_site(); let uses = quote_spanned! { defspan => @@ -467,6 +544,8 @@ pub fn savefile_abi_export(item: proc_macro::TokenStream) -> proc_macro::TokenSt let trait_type = Ident::new(symbols[1], Span::call_site()); let abi_entry = Ident::new(("abi_entry_".to_string() + symbols[1]).as_str(), Span::call_site()); + + let expanded = quote! { #[allow(clippy::double_comparisons)] const _:() = { @@ -477,7 +556,7 @@ pub fn savefile_abi_export(item: proc_macro::TokenStream) -> proc_macro::TokenSt type AbiInterface = dyn #trait_type; fn new() -> Box { - Box::new(#implementing_type::default()) + std::boxed::Box::new(#implementing_type::default()) } } #[no_mangle] @@ -490,6 +569,7 @@ pub fn savefile_abi_export(item: proc_macro::TokenStream) -> proc_macro::TokenSt expanded.into() } +#[proc_macro_error] #[proc_macro_derive( Savefile, attributes( @@ -543,6 +623,7 @@ pub fn savefile(input: proc_macro::TokenStream) -> proc_macro::TokenStream { expanded.into() } +#[proc_macro_error] #[proc_macro_derive( SavefileNoIntrospect, attributes( @@ -590,6 +671,7 @@ pub fn savefile_no_introspect(input: proc_macro::TokenStream) -> proc_macro::Tok expanded.into() } +#[proc_macro_error] #[proc_macro_derive( SavefileIntrospectOnly, attributes( @@ -691,7 +773,8 @@ fn implement_reprc_struct( let verinfo = parse_attr_tag(field.attrs); if verinfo.ignore { if expect_fast { - panic!( + abort!( + field.field_span, "The #[savefile_require_fast] attribute cannot be used for structures containing ignored fields" ); } else { @@ -705,7 +788,7 @@ fn implement_reprc_struct( if field_from_version == 0 && field_to_version == std::u32::MAX { if removed.is_removed() { if expect_fast { - panic!("The Removed type can only be used for removed fields. Use the savefile_version attribute to mark a field as only existing in previous versions."); + abort!(field.ty.span(), "The Removed type can only be used for removed fields. Use the savefile_version attribute to mark a field as only existing in previous versions."); } else { return implement_reprc_hardcoded_false(name, generics); } @@ -826,9 +909,16 @@ fn get_enum_size(attrs: &[syn::Attribute], actual_variants: usize) -> EnumSize { have_seen_explicit_size = true; } "u64" | "i64" => { - panic!("Savefile does not support enums with more than 2^32 variants.") + abort!( + metalist.path.span(), + "Savefile does not support enums with more than 2^32 variants." + ) } - _ => panic!("Unsupported repr(X) attribute on enum: {}", size_str), + _ => abort!( + metalist.path.span(), + "Unsupported repr(X) attribute on enum: {}", + size_str + ), } } } @@ -843,7 +933,7 @@ fn get_enum_size(attrs: &[syn::Attribute], actual_variants: usize) -> EnumSize { 2 } else { if actual_variants >= u32::MAX as usize { - panic!("The enum had an unreasonable number of variants"); + abort_call_site!("The enum had an unreasonable number of variants"); } 4 } @@ -854,6 +944,7 @@ fn get_enum_size(attrs: &[syn::Attribute], actual_variants: usize) -> EnumSize { explicit_size: have_seen_explicit_size, } } +#[proc_macro_error] #[proc_macro_derive( ReprC, attributes( @@ -865,7 +956,7 @@ fn get_enum_size(attrs: &[syn::Attribute], actual_variants: usize) -> EnumSize { ) )] pub fn reprc(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { - panic!("The #[derive(ReprC)] style of unsafe performance opt-in has been removed. The performance gains are now available automatically for any packed struct.") + abort_call_site!("The #[derive(ReprC)] style of unsafe performance opt-in has been removed. The performance gains are now available automatically for any packed struct.") } fn derive_reprc_new(input: DeriveInput) -> TokenStream { let name = input.ident; @@ -898,9 +989,9 @@ fn derive_reprc_new(input: DeriveInput) -> TokenStream { if !enum_size.explicit_size { if opt_in_fast { if any_fields { - panic!("The #[savefile_require_fast] requires an explicit #[repr(u8),C],#[repr(u16,C)] or #[repr(u32,C)], attribute."); + abort_call_site!("The #[savefile_require_fast] requires an explicit #[repr(u8),C],#[repr(u16,C)] or #[repr(u32,C)], attribute."); } else { - panic!("The #[savefile_require_fast] requires an explicit #[repr(u8)],#[repr(u16)] or #[repr(u32)], attribute."); + abort_call_site!("The #[savefile_require_fast] requires an explicit #[repr(u8)],#[repr(u16)] or #[repr(u32)], attribute."); } } return implement_reprc_hardcoded_false(name, input.generics); @@ -943,7 +1034,7 @@ fn derive_reprc_new(input: DeriveInput) -> TokenStream { let verinfo = parse_attr_tag(&attrs[i]); if check_is_remove(&field_types[i]).is_removed() { if verinfo.version_to == u32::MAX { - panic!("Removed fields must have a max version, provide one using #[savefile_versions=\"..N\"]") + abort!(field_types[i].span(), "Removed fields must have a max version, provide one using #[savefile_versions=\"..N\"]") } min_safe_version = min_safe_version.max(verinfo.version_to + 1); } @@ -971,7 +1062,7 @@ fn derive_reprc_new(input: DeriveInput) -> TokenStream { let verinfo = parse_attr_tag(attr); if verinfo.ignore { if opt_in_fast { - panic!( + abort_call_site!( "The #[savefile_require_fast] attribute cannot be used for structures containing ignored fields" ); } else { @@ -1060,6 +1151,7 @@ fn derive_reprc_new(input: DeriveInput) -> TokenStream { .enumerate() .map(|(field_index, field)| FieldInfo { ident: Some(field.ident.clone().expect("Expected identifier [8]")), + field_span: field.ident.as_ref().unwrap().span(), index: field_index as u32, ty: &field.ty, attrs: &field.attrs, @@ -1074,6 +1166,7 @@ fn derive_reprc_new(input: DeriveInput) -> TokenStream { .iter() .enumerate() .map(|(idx, field)| FieldInfo { + field_span: field.ty.span(), ident: None, index: idx as u32, ty: &field.ty, @@ -1087,7 +1180,7 @@ fn derive_reprc_new(input: DeriveInput) -> TokenStream { }, _ => { if opt_in_fast { - panic!("Unsupported data type"); + abort_call_site!("Unsupported data type"); } return implement_reprc_hardcoded_false(name, input.generics); } @@ -1119,7 +1212,10 @@ fn implement_introspect( for (idx, field) in field_infos.iter().enumerate() { let verinfo = parse_attr_tag(field.attrs); if verinfo.introspect_key && introspect_key.is_some() { - panic!("Type had more than one field with savefile_introspect_key - attribute"); + abort!( + field.field_span, + "Type had more than one field with savefile_introspect_key - attribute" + ); } if verinfo.introspect_ignore { continue; @@ -1221,6 +1317,7 @@ fn savefile_derive_crate_introspect(input: DeriveInput) -> TokenStream { for (idx, f) in fields_named.named.iter().enumerate() { field_infos.push(FieldInfo { ident: Some(f.ident.clone().expect("Expected identifier[9]")), + field_span: f.ident.as_ref().unwrap().span(), index: idx as u32, ty: &f.ty, attrs: &f.attrs, @@ -1251,6 +1348,7 @@ fn savefile_derive_crate_introspect(input: DeriveInput) -> TokenStream { &syn::Fields::Unnamed(ref fields_unnamed) => { for (idx, f) in fields_unnamed.unnamed.iter().enumerate() { field_infos.push(FieldInfo { + field_span: f.ty.span(), ident: None, index: idx as u32, ty: &f.ty, @@ -1337,6 +1435,7 @@ fn savefile_derive_crate_introspect(input: DeriveInput) -> TokenStream { .enumerate() .map(|(idx, field)| FieldInfo { ident: Some(field.ident.clone().expect("Expected identifier[10]")), + field_span: field.ident.as_ref().unwrap().span(), ty: &field.ty, index: idx as u32, attrs: &field.attrs, @@ -1352,6 +1451,7 @@ fn savefile_derive_crate_introspect(input: DeriveInput) -> TokenStream { .enumerate() .map(|(idx, f)| FieldInfo { ident: None, + field_span: f.ty.span(), ty: &f.ty, index: idx as u32, attrs: &f.attrs, @@ -1401,7 +1501,7 @@ fn savefile_derive_crate_introspect(input: DeriveInput) -> TokenStream { } } _ => { - panic!("Unsupported datatype"); + abort_call_site!("Unsupported datatype"); } }; @@ -1469,9 +1569,12 @@ fn implement_withschema( let field_type = &field.ty; if field_from_version == 0 && field_to_version == u32::MAX { if removed.is_removed() { - panic!("The Removed type can only be used for removed fields. Use the savefile_version attribute."); + abort!( + field.ty.span(), + "The Removed type can only be used for removed fields. Use the savefile_version attribute." + ); } - fields.push(quote_spanned!( span => #fields1.push(unsafe{#Field::unsafe_new(#name_str.to_string(), Box::new(<#field_type as #WithSchema>::schema(#local_version)), #offset)} ))); + fields.push(quote_spanned!( span => #fields1.push(unsafe{#Field::unsafe_new(#name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version)), #offset)} ))); } else { let mut version_mappings = Vec::new(); let offset = if field_to_version != u32::MAX { @@ -1486,7 +1589,7 @@ fn implement_withschema( // We don't supply offset in this case, deserialized type doesn't match field type version_mappings.push(quote!{ if #local_version >= #dt_from && local_version <= #dt_to { - #fields1.push(#Field ::new( #name_str.to_string(), Box::new(<#dt_field_type as #WithSchema>::schema(#local_version))) ); + #fields1.push(#Field ::new( #name_str.to_string(), std::boxed::Box::new(<#dt_field_type as #WithSchema>::schema(#local_version))) ); } }); } @@ -1495,7 +1598,7 @@ fn implement_withschema( #(#version_mappings)* if #local_version >= #field_from_version && #local_version <= #field_to_version { - #fields1.push(unsafe{#Field ::unsafe_new( #name_str.to_string(), Box::new(<#field_type as #WithSchema>::schema(#local_version)), #offset )} ); + #fields1.push(unsafe{#Field ::unsafe_new( #name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version)), #offset )} ); } )); } @@ -1557,7 +1660,10 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to); if field_to_version != std::u32::MAX { - panic!("Savefile automatic derive does not support removal of enum values."); + abort!( + variant.span(), + "Savefile automatic derive does not support removal of enum values." + ); } let mut field_infos = Vec::new(); @@ -1578,6 +1684,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { field_pattern.push(field_name); field_infos.push(FieldInfo { ident: Some(f.ident.clone().expect("Expected identifier[1]")), + field_span: f.ident.as_ref().unwrap().span(), ty: &f.ty, index: idx as u32, attrs: &f.attrs, @@ -1593,6 +1700,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { field_offset_extractors.push(quote!(unsafe { (#field_binding as *const _ as *const u8).offset_from(base_ptr) as usize })); field_infos.push(FieldInfo { ident: None, + field_span: f.ty.span(), index: idx as u32, ty: &f.ty, attrs: &f.attrs, @@ -1662,7 +1770,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { varbuf[3] = (variant>>24) as u8; ); } else { - panic!("Unsupported enum size: {}", enum_size.discriminant_size); + abort_call_site!("Unsupported enum size: {}", enum_size.discriminant_size); } let not_const_if_gen = if generics.params.is_empty() { quote! {const} @@ -1757,6 +1865,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { .enumerate() .map(|(idx, field)| FieldInfo { ident: Some(field.ident.clone().expect("Expected identifier[2]")), + field_span: field.ident.span(), ty: &field.ty, index: idx as u32, attrs: &field.attrs, @@ -1778,6 +1887,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { .iter() .enumerate() .map(|(idx, f)| FieldInfo { + field_span: f.ty.span(), ident: None, index: idx as u32, ty: &f.ty, @@ -1818,7 +1928,7 @@ fn savefile_derive_crate_withschema(input: DeriveInput) -> TokenStream { } } _ => { - panic!("Unsupported datatype"); + abort_call_site!("Unsupported datatype"); } }; // For debugging, uncomment to write expanded procmacro to file diff --git a/savefile-derive/src/savefile_abi.rs b/savefile-derive/src/savefile_abi.rs index b4cdd10..544439d 100644 --- a/savefile-derive/src/savefile_abi.rs +++ b/savefile-derive/src/savefile_abi.rs @@ -1,48 +1,42 @@ - +use common::{compile_time_check_reprc, compile_time_size}; +use proc_macro2::{Ident, Literal, Span, TokenStream}; +use quote::ToTokens; use std::collections::HashMap; use std::sync::atomic::AtomicU64; -use proc_macro2::{Ident, Literal, Span, TokenStream}; -use quote::{ToTokens}; -use syn::{GenericArgument, Path, PathArguments, ReturnType, Type, TypeParamBound, TypeTuple}; use syn::punctuated::Punctuated; -use common::{compile_time_check_reprc, compile_time_size}; +use syn::spanned::Spanned; +use syn::{GenericArgument, Path, PathArguments, ReturnType, Type, TypeParamBound, TypeTuple}; - - -const POINTER_SIZE:usize = std::mem::size_of::<*const ()>(); +const POINTER_SIZE: usize = std::mem::size_of::<*const ()>(); #[allow(unused)] -const FAT_POINTER_SIZE:usize = 2*POINTER_SIZE; +const FAT_POINTER_SIZE: usize = 2 * POINTER_SIZE; #[allow(unused)] -const FAT_POINTER_ALIGNMENT:usize = POINTER_SIZE; +const FAT_POINTER_ALIGNMENT: usize = POINTER_SIZE; -const MEGA_FAT_POINTER: (usize,usize) = (FAT_POINTER_SIZE + POINTER_SIZE, POINTER_SIZE); -const FAT_POINTER:(usize,usize) = (2*POINTER_SIZE,POINTER_SIZE); +const MEGA_FAT_POINTER: (usize, usize) = (FAT_POINTER_SIZE + POINTER_SIZE, POINTER_SIZE); +const FAT_POINTER: (usize, usize) = (2 * POINTER_SIZE, POINTER_SIZE); -#[derive(PartialEq,Eq,Debug,Clone,Hash)] +#[derive(PartialEq, Eq, Debug, Clone, Hash)] pub(crate) struct FnWrapperKey { fnkind: Ident, ret: Type, args: Vec, ismut: bool, - owning: bool + owning: bool, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub(crate) struct ClosureWrapperNames { trait_name: Ident, wrapper_struct_name: Ident, } fn get_type(ret_type: &ReturnType) -> Type { match ret_type { - ReturnType::Default => { - Type::Tuple(TypeTuple{ - paren_token: Default::default(), - elems: Punctuated::new() - }) - } - ReturnType::Type(_, typ) => { - (**typ).clone() - } + ReturnType::Default => Type::Tuple(TypeTuple { + paren_token: Default::default(), + elems: Punctuated::new(), + }), + ReturnType::Type(_, typ) => (**typ).clone(), } } @@ -55,23 +49,27 @@ fn emit_closure_helpers( ismut: bool, extra_definitions: &mut HashMap, fnkind: &Ident, //Fn or FnMut - owning: bool + owning: bool, ) -> ClosureWrapperNames /*wrapper name*/ { - let key = FnWrapperKey { - fnkind:fnkind.clone(), ismut, owning, + fnkind: fnkind.clone(), + ismut, + owning, args: args.iter().cloned().collect(), - ret: get_type(&return_type) + ret: get_type(&return_type), }; - if let Some((names,_)) = extra_definitions.get(&key) { + if let Some((names, _)) = extra_definitions.get(&key) { return names.clone(); } let cnt = ID_GEN.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - let temp_trait_name = Ident::new(&format!("__{}_{}", cnt, if owning {"owning_"} else {""}), Span::call_site()); + let temp_trait_name = Ident::new( + &format!("__{}_{}", cnt, if owning { "owning_" } else { "" }), + Span::call_site(), + ); let temp_trait_name_wrapper = Ident::new(&format!("{}wrapper", temp_trait_name), Span::call_site()); let names = ClosureWrapperNames { wrapper_struct_name: temp_trait_name_wrapper.clone(), - trait_name: temp_trait_name.clone() + trait_name: temp_trait_name.clone(), }; let mut formal_parameter_declarations = vec![]; @@ -139,14 +137,14 @@ fn emit_closure_helpers( pub(crate) enum ArgType { PlainData(Type), Reference(Box, bool /*ismut (only traits objects can be mut here)*/), - Str, + Str(bool/*static*/), Boxed(Box), Slice(Box), Trait(Ident, bool /*ismut self*/), Fn( TokenStream, /*full closure definition (e.g "Fn(u32)->u16")*/ Vec, /*arg types*/ - ReturnType, //Ret-type + ReturnType, //Ret-type bool, /*ismut (FnMut)*/ ), } @@ -157,44 +155,59 @@ pub(crate) struct MethodDefinitionComponents { pub(crate) caller_method_trampoline: TokenStream, } -pub(crate) fn parse_box_type(version:u32, path: &Path, method_name: &Ident, arg_name: &str, typ: &Type, - name_generator: &mut impl FnMut() -> String, - extra_definitions: &mut HashMap, - is_reference: bool, - is_mut_ref: bool, -) -> ArgType -{ - if path.segments.len()!=1 { - panic!("Savefile does not support types named 'Box', unless they are the standard type Box, and it must be specified as 'Box', without any namespace"); +pub(crate) fn parse_box_type( + version: u32, + path: &Path, + method_name: &Ident, + is_return_value: bool, + arg_name: &str, + typ: &Type, + name_generator: &mut impl FnMut() -> String, + extra_definitions: &mut HashMap, + is_reference: bool, + is_mut_ref: bool, +) -> ArgType { + + let location; + if is_return_value { + location = format!("In return value of method '{}'", method_name); + } else { + location = format!("Method '{}', argument {}", method_name, arg_name); } + + + if path.segments.len() != 1 { + abort!(path.span(), "Savefile does not support types named 'Box', unless they are the standard type Box, and it must be specified as 'Box', without any namespace"); + } + if is_reference { + abort!(path.span(), "{}. Savefile does not support references to Boxes. Just supply a reference to the inner type: {}", location, typ.to_token_stream()); + } + let last_seg = path.segments.iter().last().unwrap(); match &last_seg.arguments { PathArguments::AngleBracketed(ang) => { let first_gen_arg = ang.args.iter().next().expect("Missing generic args of Box"); if ang.args.len() != 1 { - panic!("Method {}, argument {}. Savefile requires Box arguments to have exactly one generic argument, a requirement not satisfied by type: {}", method_name, arg_name, typ.to_token_stream()); - } - if is_reference { - panic!("Method {}, argument {}. Savefile does not support references to Boxes. Just supply a reference to the inner type: {}", method_name, arg_name, typ.to_token_stream()); + abort!(ang.span(), "{}. Savefile requires Box arguments to have exactly one generic argument, a requirement not satisfied by type: {}", location, typ.to_token_stream()); } match first_gen_arg { - GenericArgument::Type(angargs) => - { + GenericArgument::Type(angargs) => { match parse_type( version, arg_name, angargs, method_name, + is_return_value, &mut *name_generator, extra_definitions, true, is_mut_ref, ){ ArgType::Boxed(_) => { - panic!("Method {}, argument {}. Savefile does not support a Box containing another Box: {}", method_name, arg_name, typ.to_token_stream()) + abort!(first_gen_arg.span(), "{}. Savefile does not support a Box containing another Box: {}", location, typ.to_token_stream()) } - ArgType::PlainData(_) | ArgType::Str => { + ArgType::PlainData(_) | ArgType::Str(_) => { return ArgType::PlainData(typ.clone()); //Box is itself a plaintype. So handle it as such. It can matter, if Box implements Serializable, when T does not. (example: str) } ArgType::Slice(slicetype) => { @@ -203,89 +216,34 @@ pub(crate) fn parse_box_type(version:u32, path: &Path, method_name: &Ident, arg_ return ArgType::Slice(slicetype); } _x => - panic!("Method {}, argument {}. Savefile does not support a Box containing a slice of anything complex, like: {}", method_name, arg_name, typ.to_token_stream()) + abort!(angargs.span(), "{}. Savefile does not support a Box containing a slice of anything complex, like: {}", location, typ.to_token_stream()) } } ArgType::Reference(_, _) => { - panic!("Method {}, argument {}. Savefile does not support a Box containing a reference, like: {} (boxing a reference is generally a useless thing to do))", method_name, arg_name, typ.to_token_stream()); + abort!(first_gen_arg.span(), "{}. Savefile does not support a Box containing a reference, like: {} (boxing a reference is generally a useless thing to do))", location, typ.to_token_stream()); } x@ArgType::Trait(_, _) | x@ArgType::Fn(_, _, _, _) => { ArgType::Boxed(Box::new(x)) } } - - /*match angargs { - Type::TraitObject(trait_obj) => { - if is_reference { - panic!("Method {}, argument {}: Reference to boxed trait object is not supported by savefile. Try using a regular reference to the box content instead.", method_name, arg_name); - } - let type_bounds: Vec<_> = trait_obj - .bounds - .iter() - .filter_map(|x| match x { - TypeParamBound::Trait(t) => Some( - t.path - .segments - .iter() - .last() - .cloned() - .expect("Missing bounds of Box trait object") - .ident - .clone(), - ), - TypeParamBound::Lifetime(_) => None, - }) - .collect(); - if type_bounds.len() == 0 { - panic!("Method {}, argument {}, unsupported Box-type. Only Box is supported. Encountered zero traits in Box.", method_name, arg_name); - } - if type_bounds.len() > 1 { - panic!("Method {}, argument {}, unsupported Box-type. Only Box is supported. Encountered multiple traits in Box: {:?}", method_name, arg_name, trait_obj); - } - if trait_obj.dyn_token.is_none() { - panic!("Method {}, argument {}, unsupported Box-type. Only Box is supported.", method_name, arg_name) - } - let bound = type_bounds.into_iter().next().expect("Internal error, missing bounds"); - - return ArgType::BoxedTrait(bound); - } - _ => { - match parse_type( - version, - arg_name, - angargs, - method_name, - &mut *name_generator, - extra_definitions, - is_reference, - is_mut_ref, - ) { - ArgType::PlainData(_plain) => { - return ArgType::PlainData(typ.clone()); - } - _ => { - panic!( - "Method {}, argument {}, unsupported Box-type: {:?}", - method_name, arg_name, typ - ); - } - } - } - */ - }, + } _ => { - panic!( - "Method {}, argument {}, unsupported Box-type: {}", - method_name, arg_name, typ.to_token_stream() + abort!( + typ.span(), + "{}, unsupported Box-type: {}", + location, + typ.to_token_stream() ); } } } _ => { - panic!( - "Method {}, argument {}, unsupported Box-type: {}", - method_name, arg_name, typ.to_token_stream() + abort!( + typ.span(), + "{}, unsupported Box-type: {}", + location, + typ.to_token_stream() ); } } @@ -297,11 +255,19 @@ fn parse_type( arg_name: &str, typ: &Type, method_name: &Ident, + is_return_value: bool, name_generator: &mut impl FnMut() -> String, extra_definitions: &mut HashMap, is_reference: bool, is_mut_ref: bool, ) -> ArgType { + let location; + if is_return_value { + location = format!("In return value of method '{}'", method_name); + } else { + location = format!("Method '{}', argument {}", method_name, arg_name); + } + let rawtype; match typ { Type::Tuple(tup) if tup.elems.is_empty() => { @@ -309,14 +275,23 @@ fn parse_type( //argtype = ArgType::PlainData(typ.to_token_stream()); } Type::Reference(typref) => { + let is_static_lifetime; if typref.lifetime.is_some() { - panic!( - "Method {}, argument {}: Specifying lifetimes is not supported.", - method_name, arg_name - ); + match &typref.lifetime { + Some(lifetime) if lifetime.ident == "static" => { + is_static_lifetime = true; + } + _ => abort!( + typref.lifetime.span(), + "{}: Specifying lifetimes is not supported by Savefile-Abi.", + location, + ) + } + } else { + is_static_lifetime = false; } if is_reference { - panic!("Method {}, argument {}: Method arguments cannot be reference to reference in Savefile-abi. Try removing a '&' from the type: {}", method_name, arg_name, typ.to_token_stream()); + abort!(typ.span(), "{}: Method arguments cannot be reference to reference in savefile-abi. Try removing a '&' from the type: {}", location, typ.to_token_stream()); } let inner = parse_type( @@ -324,48 +299,51 @@ fn parse_type( arg_name, &typref.elem, method_name, + is_return_value, &mut *name_generator, extra_definitions, true, - typref.mutability.is_some()); - if let ArgType::Str = inner { - return ArgType::Str; //Str is a special case, it is always a reference + typref.mutability.is_some(), + ); + if let ArgType::Str(_) = inner { + return ArgType::Str(is_static_lifetime); //Str is a special case, it is always a reference } return ArgType::Reference(Box::new(inner), typref.mutability.is_some()); } Type::Tuple(tuple) => { if tuple.elems.len() > 3 { - panic!("Savefile presently only supports tuples up to 3 members. Either change to using a struct, or file an issue on savefile!"); + abort!(tuple.span(), "Savefile presently only supports tuples up to 3 members. Either change to using a struct, or file an issue on savefile!"); } rawtype = typ; } Type::Slice(slice) => { if !is_reference { - panic!( - "Method {}, argument {}: Slices must always be behind references. Try adding a '&' to the type: {}", - method_name, - arg_name, + abort!( + slice.span(), + "{}: Slices must always be behind references. Try adding a '&' to the type: {}", + location, typ.to_token_stream() ); } if is_mut_ref { - panic!("Method {}, argument {}: Mutable refernces are not supported by Savefile-abi, except for FnMut-trait objects. {}", method_name, arg_name, typ.to_token_stream()); + abort!(typ.span(), "{}: Mutable refernces are not supported by savefile-abi, except for FnMut-trait objects. {}", location, typ.to_token_stream()); } let argtype = parse_type( version, arg_name, &slice.elem, method_name, + is_return_value, &mut *name_generator, extra_definitions, is_reference, - is_mut_ref + is_mut_ref, ); return ArgType::Slice(Box::new(argtype)); } Type::TraitObject(trait_obj) => { if !is_reference { - panic!("Method {}, argument {}: Trait objects must always be behind references. Try adding a '&' to the type: {}", method_name, arg_name, typ.to_token_stream()); + abort!(trait_obj.span(), "{}: Trait objects must always be behind references. Try adding a '&' to the type: {}", location, typ.to_token_stream()); } if trait_obj.dyn_token.is_some() { let type_bounds: Vec<_> = trait_obj @@ -378,39 +356,43 @@ fn parse_type( .iter() .last() .expect("Missing bounds of Box trait object"), - TypeParamBound::Lifetime(_) => { - panic!( - "Method {}, argument {}: Specifying lifetimes is not supported.", - method_name, arg_name + TypeParamBound::Lifetime(lt) => { + abort!( + lt.span(), + "{}: Specifying lifetimes is not supported by Savefile-Abi.", + location, ); } }) .collect(); if type_bounds.len() == 0 { - panic!("Method {}, argument {}, unsupported trait object reference. Only &dyn Trait is supported. Encountered zero traits.", method_name, arg_name); + abort!(trait_obj.bounds.span(), "{}, unsupported trait object reference. Only &dyn Trait is supported. Encountered zero traits.", location); } if type_bounds.len() > 1 { - panic!("Method {}, argument {}, unsupported Box-type. Only &dyn Trait> is supported. Encountered multiple traits: {:?}", method_name, arg_name, trait_obj); + abort!(trait_obj.bounds.span(), "{}, unsupported Box-type. Only &dyn Trait> is supported. Encountered multiple traits: {:?}", location, trait_obj); } let bound = type_bounds.into_iter().next().expect("Internal error, missing bounds"); if bound.ident == "Fn" || bound.ident == "FnMut" || bound.ident == "FnOnce" { if bound.ident == "FnOnce" { - panic!( - "Method {}, argument {}, FnOnce is not supported. Maybe you can use FnMut instead?", - method_name, arg_name + abort!( + bound.ident.span(), + "{}, FnOnce is not supported. Maybe you can use FnMut instead?", + location, ); } + if bound.ident == "Fn" && is_mut_ref { + abort!(bound.ident.span(), "{}: Mutable references to Fn are not supported by savefile-abi. Try using a non-mutable reference instead..", location); + } if bound.ident == "FnMut" && !is_mut_ref { - panic!("Method {}, argument {}: When using FnMut, it must be referenced using &mut, not &. Otherwise, it is impossible to call.", method_name, arg_name); + abort!(bound.ident.span(), "{}: When using FnMut, it must be referenced using &mut, not &. Otherwise, it is impossible to call.", location); } let fn_decl = bound.to_token_stream(); match &bound.arguments { PathArguments::Parenthesized(pararg) => { - /*let temp_name = - Ident::new(&format!("{}_{}", &name_generator(), arg_name), Span::call_site());*/ + Ident::new(&format!("{}_{}", &name_generator(), arg_name), Span::call_site());*/ return ArgType::Fn( fn_decl, pararg.inputs.iter().cloned().collect(), @@ -419,49 +401,71 @@ fn parse_type( ); } _ => { - panic!("Fn/FnMut arguments must be enclosed in parenthesis") + abort!( + bound.arguments.span(), + "Fn/FnMut arguments must be enclosed in parenthesis" + ) } } } else { return ArgType::Trait(bound.ident.clone(), is_mut_ref); } } else { - panic!( - "Method {}, argument {}, reference to trait objects without 'dyn' are not supported.", - method_name, arg_name + abort!( + trait_obj.span(), + "{}, reference to trait objects without 'dyn' are not supported.", + location, ); } } Type::Path(path) => { let last_seg = path.path.segments.iter().last().expect("Missing path segments"); if last_seg.ident == "str" { - if path.path.segments.len()!=1 { - panic!("Savefile does not support types named 'str', unless they are the standard type str, and it must be specified as 'str', without any namespace"); + if path.path.segments.len() != 1 { + abort!(path.path.segments.span(), "Savefile does not support types named 'str', unless they are the standard type str, and it must be specified as 'str', without any namespace"); } if !is_reference { - panic!("Savefile does not support the type 'str' (but it does support '&str')."); + abort!( + path.span(), + "Savefile does not support the type 'str' (but it does support '&str')." + ); } - return ArgType::Str; - } - else - if last_seg.ident == "Box" { + return ArgType::Str(false); // This is a hack. ArgType::Str means '&str' everywhere but here, where it means 'str' + } else if last_seg.ident == "Box" { if is_reference { - panic!("Savefile does not support reference to Box. This is also generally not very useful, just use a regular reference for arguments."); + abort!(last_seg.ident.span(), "Savefile does not support reference to Box. This is also generally not very useful, just use a regular reference for arguments."); } - return parse_box_type(version,&path.path, method_name, arg_name, typ, name_generator, extra_definitions, is_reference, is_mut_ref); + return parse_box_type( + version, + &path.path, + method_name, + is_return_value, + arg_name, + typ, + name_generator, + extra_definitions, + is_reference, + is_mut_ref, + ); } else { rawtype = typ; } } _ => { - panic!( - "Method {}, argument {}, unsupported type: {:?}", - method_name, arg_name, typ + abort!( + typ.span(), + "{}, type is unsupported by savefile-abi: {}", + location, + typ.to_token_stream() ); } } if is_mut_ref { - panic!("Method {}, argument {}: Mutable references are not supported by Savefile-abi (except for trait objects): {}", method_name, arg_name, typ.to_token_stream()); + abort!( + typ.span(), + "{}: Mutable references are not supported by savefile-abi (except for trait objects)", + location, + ); } ArgType::PlainData(rawtype.clone()) } @@ -478,9 +482,9 @@ struct TypeInstruction { schema: TokenStream, arg_type1: TokenStream, - known_size_align1: Option<(usize,usize)>, + known_size_align1: Option<(usize, usize)>, /// The size and alignment of a pointer to this type - known_size_align_of_pointer1: Option<(usize,usize)>, + known_size_align_of_pointer1: Option<(usize, usize)>, /// The type that this parameter is primarily deserialized into, on the /// deserialized side (i.e, callee for arguments, caller for return value); deserialized_type: TokenStream, @@ -496,30 +500,45 @@ fn mutsymbol(ismut: bool) -> TokenStream { } impl ArgType { - fn get_instruction(&self, version: u32, arg_index: Option, arg_orig_name: &str, arg_name: &TokenStream, nesting_level: u32, take_ownership: bool, - extra_definitions: &mut HashMap + fn get_instruction( + &self, + version: u32, + arg_index: Option, + arg_orig_name: &str, + arg_name: &TokenStream, + nesting_level: u32, + take_ownership: bool, + extra_definitions: &mut HashMap, ) -> TypeInstruction { - let temp_arg_name = Ident::new(&format!("temp_{}_{}", arg_orig_name,nesting_level), Span::call_site()); + let temp_arg_name = Ident::new(&format!("temp_{}_{}", arg_orig_name, nesting_level), Span::call_site()); let layout_compatible = if let Some(arg_index) = arg_index { quote!(compatibility_mask&(1<<#arg_index) != 0) } else { - quote!( false ) + quote!(false) }; match self { ArgType::Reference(arg_type, is_mut) => { - //let mutsym = mutsymbol(*is_mut); - let TypeInstruction{ + let TypeInstruction { callee_trampoline_temp_variable_declaration1, callee_trampoline_variable_deserializer1, caller_arg_serializer_temp1, caller_arg_serializer1, schema, arg_type1, - known_size_align1:_, - known_size_align_of_pointer1, deserialized_type - } = arg_type.get_instruction(version, arg_index, arg_orig_name, arg_name, nesting_level+1, false, extra_definitions); + known_size_align1: _, + known_size_align_of_pointer1, + deserialized_type, + } = arg_type.get_instruction( + version, + arg_index, + arg_orig_name, + arg_name, + nesting_level + 1, + false, + extra_definitions, + ); let known_size_align1 = match &**arg_type { ArgType::PlainData(plain) => { @@ -533,32 +552,29 @@ impl ArgType { None } } - ArgType::Boxed(inner) => { - match &**inner { - ArgType::Fn(..) | ArgType::Trait(..) => Some(MEGA_FAT_POINTER), - _ => None, - } - } - ArgType::Fn(..) | ArgType::Trait(..) => Some(MEGA_FAT_POINTER), - ArgType::Str => None, + ArgType::Boxed(inner) => match &**inner { + ArgType::Fn(..) | ArgType::Trait(..) => Some(MEGA_FAT_POINTER), + _ => None, + }, + ArgType::Fn(..) | ArgType::Trait(..) => Some(MEGA_FAT_POINTER), + ArgType::Str(_) => None, ArgType::Reference(..) => None, ArgType::Slice(_) => None, }; - let (mutsymbol,read_raw_ptr) = if *is_mut { - (quote!( mut ), quote!( read_raw_ptr_mut )) + let (mutsymbol, read_raw_ptr) = if *is_mut { + (quote!(mut), quote!(read_raw_ptr_mut)) } else { - (quote!( ), quote!( read_raw_ptr )) + (quote!(), quote!(read_raw_ptr)) }; - TypeInstruction { callee_trampoline_temp_variable_declaration1: quote! { #callee_trampoline_temp_variable_declaration1 let #mutsymbol #temp_arg_name; }, deserialized_type, - arg_type1: quote!( & arg_type1 ), + arg_type1: quote!(&arg_type1), callee_trampoline_variable_deserializer1: quote! { if #layout_compatible { unsafe { &#mutsymbol *(deserializer. #read_raw_ptr ::<#arg_type1>()?) } @@ -576,16 +592,16 @@ impl ArgType { #caller_arg_serializer1 } }, - schema: quote! {Schema::Reference(Box::new(#schema))}, + schema: quote! {Schema::Reference(std::boxed::Box::new(#schema))}, known_size_align1, known_size_align_of_pointer1: None, //Pointer to pointer not even supported } } - ArgType::Str => { + ArgType::Str(_) => { TypeInstruction { //callee_trampoline_real_method_invocation_argument1: quote! {&#arg_name}, callee_trampoline_temp_variable_declaration1: quote! {}, - deserialized_type: quote!{String}, + deserialized_type: quote! {String}, callee_trampoline_variable_deserializer1: quote! { { let ptr = deserializer.read_ptr()? as *const u8; @@ -603,26 +619,33 @@ impl ArgType { } }, //caller_fn_arg1: quote! {#arg_name : &str}, - schema: quote!( Schema::Str ), + schema: quote!(Schema::Str), - arg_type1: quote!( str ), + arg_type1: quote!(str), known_size_align1: None, known_size_align_of_pointer1: None, } } ArgType::Slice(arg_type) => { - let TypeInstruction{ + let TypeInstruction { callee_trampoline_temp_variable_declaration1, - callee_trampoline_variable_deserializer1:_, + callee_trampoline_variable_deserializer1: _, caller_arg_serializer_temp1, - caller_arg_serializer1:_, + caller_arg_serializer1: _, schema, arg_type1, known_size_align1, - known_size_align_of_pointer1:_, - deserialized_type:_ - } = arg_type.get_instruction(version, arg_index,arg_orig_name, arg_name, nesting_level+1, false, extra_definitions); - + known_size_align_of_pointer1: _, + deserialized_type: _, + } = arg_type.get_instruction( + version, + arg_index, + arg_orig_name, + arg_name, + nesting_level + 1, + false, + extra_definitions, + ); TypeInstruction { //callee_trampoline_real_method_invocation_argument1: quote! {&#arg_name}, @@ -630,7 +653,7 @@ impl ArgType { #callee_trampoline_temp_variable_declaration1 let #temp_arg_name; }, - deserialized_type: quote!{Vec<_>}, + deserialized_type: quote! {Vec<_>}, callee_trampoline_variable_deserializer1: quote! { { #temp_arg_name = deserialize_slice_as_vec::<_,#arg_type1>(&mut deserializer)?; @@ -642,72 +665,86 @@ impl ArgType { (#arg_name).serialize(&mut serializer) }, //we only support slices containing savefile-serializable stuff, so we don't forward to the item type here //caller_fn_arg1: quote! {#arg_name : &[#arg_type]}, - schema: quote!( Schema::Slice(Box::new(#schema)) ), + schema: quote!( Schema::Slice(std::boxed::Box::new(#schema)) ), arg_type1: quote!( [#arg_type1] ), - known_size_align1: if known_size_align1.is_some() {Some(FAT_POINTER)} else {None}, - known_size_align_of_pointer1: None, - } - } - ArgType::PlainData(arg_type) => { - - TypeInstruction { - deserialized_type: quote!{#arg_type}, - callee_trampoline_temp_variable_declaration1: quote!(), - callee_trampoline_variable_deserializer1: quote! { - <#arg_type as Deserialize>::deserialize(&mut deserializer)? - }, - caller_arg_serializer_temp1: quote!(), - caller_arg_serializer1: quote! { - #arg_name.serialize(&mut serializer) + known_size_align1: if known_size_align1.is_some() { + Some(FAT_POINTER) + } else { + None }, - schema: quote!( <#arg_type as WithSchema>::schema(version) ), - known_size_align1: if compile_time_check_reprc(arg_type) { - compile_time_size(arg_type) - } else { None }, - arg_type1: arg_type.to_token_stream(), known_size_align_of_pointer1: None, } } + ArgType::PlainData(arg_type) => TypeInstruction { + deserialized_type: quote! {#arg_type}, + callee_trampoline_temp_variable_declaration1: quote!(), + callee_trampoline_variable_deserializer1: quote! { + <#arg_type as Deserialize>::deserialize(&mut deserializer)? + }, + caller_arg_serializer_temp1: quote!(), + caller_arg_serializer1: quote! { + #arg_name.serialize(&mut serializer) + }, + schema: quote!( <#arg_type as WithSchema>::schema(version) ), + known_size_align1: if compile_time_check_reprc(arg_type) { + compile_time_size(arg_type) + } else { + None + }, + arg_type1: arg_type.to_token_stream(), + known_size_align_of_pointer1: None, + }, ArgType::Boxed(inner_arg_type) => { - let TypeInstruction{ + let TypeInstruction { callee_trampoline_temp_variable_declaration1, callee_trampoline_variable_deserializer1, caller_arg_serializer_temp1, caller_arg_serializer1, schema, arg_type1, - known_size_align1:_, - known_size_align_of_pointer1:_, - deserialized_type - } = inner_arg_type.get_instruction(version, arg_index, arg_orig_name,"e!( #arg_name ), nesting_level+1, true, extra_definitions); + known_size_align1: _, + known_size_align_of_pointer1: _, + deserialized_type, + } = inner_arg_type.get_instruction( + version, + arg_index, + arg_orig_name, + "e!( #arg_name ), + nesting_level + 1, + true, + extra_definitions, + ); TypeInstruction { //deserialized_type: quote!{Box>}, - deserialized_type: quote!{Box<#deserialized_type>}, + deserialized_type: quote! {Box<#deserialized_type>}, callee_trampoline_temp_variable_declaration1: quote! { #callee_trampoline_temp_variable_declaration1 }, callee_trampoline_variable_deserializer1: quote! { - Box::new( #callee_trampoline_variable_deserializer1 ) + std::boxed::Box::new( #callee_trampoline_variable_deserializer1 ) }, caller_arg_serializer_temp1, caller_arg_serializer1, - schema: quote!( Schema::Boxed( Box::new(#schema) ) ), + schema: quote!( Schema::Boxed( std::boxed::Box::new(#schema) ) ), arg_type1: quote!( Box<#arg_type1> ), known_size_align1: None, known_size_align_of_pointer1: None, } } ArgType::Trait(trait_name, ismut) => { - let trait_type = trait_name; let newsymbol = quote! {new_from_ptr}; - let owning = if take_ownership {quote!( Owning::Owned )} else {quote!(Owning::NotOwned)}; + let owning = if take_ownership { + quote!(Owning::Owned) + } else { + quote!(Owning::NotOwned) + }; TypeInstruction { - deserialized_type: quote!{ AbiConnection }, + deserialized_type: quote! { AbiConnection }, callee_trampoline_temp_variable_declaration1: quote! {}, callee_trampoline_variable_deserializer1: quote! { { @@ -722,7 +759,7 @@ impl ArgType { }, schema: quote!( Schema::Trait(#ismut, ::get_definition(version)) ), arg_type1: quote!( dyn #trait_name ), - known_size_align1: Some((FAT_POINTER_SIZE+POINTER_SIZE, FAT_POINTER_ALIGNMENT)), + known_size_align1: Some((FAT_POINTER_SIZE + POINTER_SIZE, FAT_POINTER_ALIGNMENT)), known_size_align_of_pointer1: None, } } @@ -730,19 +767,18 @@ impl ArgType { let temp_arg_name2 = Ident::new(&format!("temp2_{}", arg_orig_name), Span::call_site()); let temp_arg_ser_name = Ident::new(&format!("temp_ser_{}", arg_orig_name), Span::call_site()); - let wrapper_names = emit_closure_helpers( version, args, ret_type.clone(), *ismut, extra_definitions, - &Ident::new(if *ismut {"FnMut"} else {"Fn"}, Span::call_site()), - take_ownership + &Ident::new(if *ismut { "FnMut" } else { "Fn" }, Span::call_site()), + take_ownership, ); let temp_trait_name_wrapper = wrapper_names.wrapper_struct_name; - let temp_trait_type= wrapper_names.trait_name; + let temp_trait_type = wrapper_names.trait_name; //let temp_trait_name_wrapper = Ident::new(&format!("{}_wrapper", temp_trait_type), Span::call_site()); let mutsymbol = if *ismut { @@ -772,20 +808,24 @@ impl ArgType { id }) .collect(); - let owning = if take_ownership {quote!( Owning::Owned )} else {quote!(Owning::NotOwned)}; + let owning = if take_ownership { + quote!(Owning::Owned) + } else { + quote!(Owning::NotOwned) + }; let arg_access = if take_ownership { - quote!{ + quote! { #arg_name } } else { - quote!{ + quote! { #arg_name as *#mutorconst _ } }; let arg_make_ptr = if take_ownership { quote! { - Box::into_raw(Box::new(#temp_arg_ser_name)) + std::boxed::Box::into_raw(Box::new(#temp_arg_ser_name)) } } else { quote! { @@ -794,7 +834,7 @@ impl ArgType { }; TypeInstruction { - deserialized_type: quote!{AbiConnection::}, + deserialized_type: quote! {AbiConnection::}, callee_trampoline_temp_variable_declaration1: quote! { let #mutsymbol #temp_arg_name; let #mutsymbol #temp_arg_name2; @@ -806,7 +846,7 @@ impl ArgType { #temp_arg_name2 } }, - caller_arg_serializer_temp1: quote!{ + caller_arg_serializer_temp1: quote! { let #mutsymbol #temp_arg_ser_name; }, @@ -821,7 +861,7 @@ impl ArgType { arg_type1: quote! {dyn #fndef }, schema: quote!( Schema::FnClosure(#ismut, ::get_definition(version)) ), //arg_type1: Default::default(), - known_size_align1: Some((FAT_POINTER_SIZE+POINTER_SIZE, FAT_POINTER_ALIGNMENT)), + known_size_align1: Some((FAT_POINTER_SIZE + POINTER_SIZE, FAT_POINTER_ALIGNMENT)), known_size_align_of_pointer1: None, } } @@ -856,46 +896,53 @@ pub(super) fn generate_method_definitions( let mut compile_time_known_size = Some(0); for (arg_index, (arg_name, typ)) in args.iter().enumerate() { + let argtype = parse_type( version, &arg_name.to_string(), typ, &method_name, + false, &mut *name_generator, extra_definitions, false, - false, + false ); callee_trampoline_variable_declaration.push(quote! {let #arg_name;}); - let instruction = argtype.get_instruction(version, Some(arg_index), &arg_name.to_string(),&arg_name.to_token_stream(), 0, true, extra_definitions); + let instruction = argtype.get_instruction( + version, + Some(arg_index), + &arg_name.to_string(), + &arg_name.to_token_stream(), + 0, + true, + extra_definitions, + ); caller_arg_serializers_temp.push(instruction.caller_arg_serializer_temp1); callee_trampoline_real_method_invocation_arguments.push( - quote! {#arg_name} - //instruction.callee_trampoline_real_method_invocation_argument1 + quote! {#arg_name}, //instruction.callee_trampoline_real_method_invocation_argument1 ); callee_trampoline_temp_variable_declaration.push(instruction.callee_trampoline_temp_variable_declaration1); let deserializer_expression = instruction.callee_trampoline_variable_deserializer1; - callee_trampoline_variable_deserializer.push( quote!( #arg_name = #deserializer_expression ; ) ); + callee_trampoline_variable_deserializer.push(quote!( #arg_name = #deserializer_expression ; )); let arg_serializer = instruction.caller_arg_serializer1; - caller_arg_serializers.push( - quote!{ - #arg_serializer.expect("Failed while serializing"); - } - ); - caller_fn_arg_list.push(quote!( #arg_name: #typ ));//instruction.caller_fn_arg1); + caller_arg_serializers.push(quote! { + #arg_serializer.expect("Failed while serializing"); + }); + caller_fn_arg_list.push(quote!( #arg_name: #typ )); //instruction.caller_fn_arg1); let schema = instruction.schema; //let can_be_sent_as_ref = instruction.can_be_sent_as_ref; - metadata_arguments.push(quote!{ + metadata_arguments.push(quote! { AbiMethodArgument { schema: #schema, } }); if let Some(total_size) = &mut compile_time_known_size { - if let Some((known_size,_known_align)) = instruction.known_size_align1 { + if let Some((known_size, _known_align)) = instruction.known_size_align1 { *total_size += known_size; } else { compile_time_known_size = None; @@ -920,25 +967,57 @@ pub(super) fn generate_method_definitions( }; let return_value_schema; - let caller_return_type; - let ret_deserializer ; + let ret_deserializer; let ret_temp_decl; let ret_serialize; let result_default; let return_ser_temp; if no_return { - return_value_schema = quote!( <() as WithSchema>::schema(0) ); - ret_deserializer = quote!( () ); //Zero-sized, no deserialize actually needed - ret_serialize = quote!( () ); - caller_return_type = quote!( () ); + return_value_schema = quote!(<() as WithSchema>::schema(0)); + ret_deserializer = quote!(()); //Zero-sized, no deserialize actually needed + ret_serialize = quote!(()); + caller_return_type = quote!(()); ret_temp_decl = quote!(); return_ser_temp = quote!(); - result_default = quote!( MaybeUninit::>::new(Ok(())) ); //Safe, does not need drop and does not allocate + result_default = quote!( MaybeUninit::>::new(Ok(())) ); + //Safe, does not need drop and does not allocate } else { - let parsed_ret_type = parse_type(version, "___retval",&ret_type,&method_name,name_generator,extra_definitions,false,false); - let instruction = parsed_ret_type.get_instruction(version, None, "ret", &Ident::new("ret", Span::call_site()).to_token_stream(), 0, true, extra_definitions); + let parsed_ret_type = parse_type( + version, + "___retval", + &ret_type, + &method_name, + true, + name_generator, + extra_definitions, + false, + false, + ); + if let ArgType::Reference(..) = &parsed_ret_type { + abort!( + ret_type.span(), + "Method '{}': savefile-abi does not support methods returning references.", + method_name + ); + } + if let ArgType::Str(false) = &parsed_ret_type { + abort!( + ret_type.span(), + "Method '{}': savefile-abi does not support methods returning &str. Use \"String\" or \"&'static str\" instead", + method_name + ); + } + let instruction = parsed_ret_type.get_instruction( + version, + None, + "ret", + &Ident::new("ret", Span::call_site()).to_token_stream(), + 0, + true, + extra_definitions, + ); caller_return_type = instruction.deserialized_type; return_value_schema = instruction.schema; return_ser_temp = instruction.caller_arg_serializer_temp1; @@ -951,7 +1030,6 @@ pub(super) fn generate_method_definitions( result_default = quote!( MaybeUninit::>::uninit() ); }; - let arg_buffer; let data_as_ptr; let data_length; @@ -962,23 +1040,23 @@ pub(super) fn generate_method_definitions( // for many more types. But we can at least do it for the most simple. let compile_time_known_size = compile_time_known_size + 4; //Space for 'version' - arg_buffer = quote!{ + arg_buffer = quote! { let mut rawdata = [0u8;#compile_time_known_size]; let mut data = Cursor::new(&mut rawdata[..]); }; - data_as_ptr = quote!( rawdata[..].as_ptr() ); + data_as_ptr = quote!(rawdata[..].as_ptr()); data_length = quote!( #compile_time_known_size ); } else { arg_buffer = quote!( let mut data = FlexBuffer::new(); ); - data_as_ptr = quote!( data.as_ptr() as *const u8 ); - data_length = quote!( data.len() ); - + data_as_ptr = quote!(data.as_ptr() as *const u8); + data_length = quote!(data.len()); } - let _ = caller_return_type; let caller_method_trampoline = quote! { + // TODO: Determine if we should use inline here or not? #[inline] + #[inline] fn #method_name(& #receiver_mut self, #(#caller_fn_arg_list,)*) #ret_declaration { let info: &AbiConnectionMethod = &self.template.methods[#method_number as usize]; @@ -1045,38 +1123,36 @@ pub(super) fn generate_method_definitions( } }; - - let handle_retval; if no_return { handle_retval = quote!(); } else { - let ret_buffer; let data_as_ptr; let data_length; - let known_size = compile_time_check_reprc(&ret_type).then_some(compile_time_size(&ret_type)).flatten(); - if let Some((compile_time_known_size,_align)) = known_size { + let known_size = compile_time_check_reprc(&ret_type) + .then_some(compile_time_size(&ret_type)) + .flatten(); + if let Some((compile_time_known_size, _align)) = known_size { // If we have simple type such as u8, u16 etc, we can sometimes // know at compile-time what the size of the args will be. // If the rust-compiler offered 'introspection', we could do this // for many more types. But we can at least do it for the most simple. let compile_time_known_size = compile_time_known_size + 4; //Space for 'version' - ret_buffer = quote!{ - let mut rawdata = [0u8;#compile_time_known_size]; - let mut data = Cursor::new(&mut rawdata[..]); - }; - data_as_ptr = quote!( rawdata[..].as_ptr() ); + ret_buffer = quote! { + let mut rawdata = [0u8;#compile_time_known_size]; + let mut data = Cursor::new(&mut rawdata[..]); + }; + data_as_ptr = quote!(rawdata[..].as_ptr()); data_length = quote!( #compile_time_known_size ); } else { ret_buffer = quote!( let mut data = FlexBuffer::new(); ); - data_as_ptr = quote!( data.as_ptr() as *const u8 ); - data_length = quote!( data.len() ); - + data_as_ptr = quote!(data.as_ptr() as *const u8); + data_length = quote!(data.len()); } - handle_retval = quote!{ + handle_retval = quote! { #ret_buffer let mut serializer = Serializer { writer: &mut data, diff --git a/savefile-derive/src/serialize.rs b/savefile-derive/src/serialize.rs index 325d76c..e7cde57 100644 --- a/savefile-derive/src/serialize.rs +++ b/savefile-derive/src/serialize.rs @@ -4,6 +4,7 @@ use syn::DeriveInput; use common::{get_extra_where_clauses, parse_attr_tag, FieldInfo}; use get_enum_size; use implement_fields_serialize; +use syn::spanned::Spanned; pub(super) fn savefile_derive_crate_serialize(input: DeriveInput) -> TokenStream { let name = input.ident; @@ -73,6 +74,7 @@ pub(super) fn savefile_derive_crate_serialize(input: DeriveInput) -> TokenStream .enumerate() .map(|(field_index, field)| FieldInfo { ident: Some(field.ident.clone().expect("Expected identifier[4]")), + field_span: field.ident.as_ref().unwrap().span(), index: field_index as u32, ty: &field.ty, attrs: &field.attrs, @@ -95,6 +97,7 @@ pub(super) fn savefile_derive_crate_serialize(input: DeriveInput) -> TokenStream .iter() .enumerate() .map(|(idx, field)| FieldInfo { + field_span: field.ty.span(), ident: Some(syn::Ident::new( // We bind the tuple field to a real name, like x0, x1 etc. &("x".to_string() + &idx.to_string()), @@ -162,6 +165,7 @@ pub(super) fn savefile_derive_crate_serialize(input: DeriveInput) -> TokenStream .enumerate() .map(|(field_index, field)| FieldInfo { ident: Some(field.ident.clone().expect("Identifier[5]")), + field_span: field.ident.as_ref().unwrap().span(), ty: &field.ty, index: field_index as u32, attrs: &field.attrs, @@ -178,6 +182,7 @@ pub(super) fn savefile_derive_crate_serialize(input: DeriveInput) -> TokenStream .iter() .enumerate() .map(|(field_index, field)| FieldInfo { + field_span: field.ty.span(), ident: None, ty: &field.ty, index: field_index as u32, @@ -213,7 +218,7 @@ pub(super) fn savefile_derive_crate_serialize(input: DeriveInput) -> TokenStream } } _ => { - panic!("Unsupported data type"); + abort_call_site!("Unsupported data type"); } }; diff --git a/savefile-min-build/Cargo.toml b/savefile-min-build/Cargo.toml index f237656..c5dd0eb 100644 --- a/savefile-min-build/Cargo.toml +++ b/savefile-min-build/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -savefile = { path = "../savefile", features = ["derive"], default-features = false } +savefile = { path = "../savefile", default-features = false } savefile-abi = { path = "../savefile-abi" } savefile-derive = {path = "../savefile-derive"} diff --git a/savefile-min-build/src/lib.rs b/savefile-min-build/src/lib.rs index 85efa39..18c49d0 100644 --- a/savefile-min-build/src/lib.rs +++ b/savefile-min-build/src/lib.rs @@ -1,37 +1,13 @@ extern crate savefile_abi; extern crate savefile_derive; -use std::collections::HashMap; -use savefile::prelude::*; -use savefile::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt::Debug; -use std::io::{BufWriter, Cursor, Write}; -use savefile_abi::AbiConnection; use savefile_derive::savefile_abi_exportable; - - - -/* - -include!("__0_owning_.rs"); -include!("__1_.rs"); -include!("__2_.rs"); -include!("__3_.rs"); -include!("AdvancedTestInterface.rs"); -*/ - -/* #[savefile_abi_exportable(version = 0)] -pub trait AdvancedTestInterface { - fn count_chars_str(&self, x: &str) -> usize; -}*/ +pub trait ExampleTrait { + fn get(&mut self) -> &'static str; +} #[test] -fn test_call_many_callbacks() { - /* let boxed: Box = Box::new(AdvancedTestInterfaceImpl {}); - let mut conn = AbiConnection::from_boxed_trait(boxed).unwrap(); - let temp = conn.get(); - assert_eq!(temp, 42);*/ -} +fn dummy_test() {} diff --git a/savefile-test/Cargo.toml b/savefile-test/Cargo.toml index 820961f..3c67e67 100644 --- a/savefile-test/Cargo.toml +++ b/savefile-test/Cargo.toml @@ -12,7 +12,7 @@ nightly=["savefile/nightly"] [dependencies] savefile = { path = "../savefile", features = ["size_sanity_checks", "encryption", "compression","bit-set","bit-vec","rustc-hash","serde_derive", "quickcheck"]} -savefile-derive = { path = "../savefile-derive", version = "=0.17.0-beta.12" } +savefile-derive = { path = "../savefile-derive", version = "=0.17.0-beta.13" } savefile-abi = { path = "../savefile-abi" } bit-vec = "0.6" arrayvec="0.7" @@ -33,5 +33,3 @@ insta = { version = "1.38.0", features = ["yaml"] } [build-dependencies] rustc_version="0.4" - - diff --git a/savefile-test/src/lib.rs b/savefile-test/src/lib.rs index cf05c90..adeec2f 100644 --- a/savefile-test/src/lib.rs +++ b/savefile-test/src/lib.rs @@ -828,6 +828,7 @@ use arrayvec::ArrayString; use quickcheck::{Arbitrary, Gen}; use rustc_hash::{FxHashMap, FxHashSet}; use savefile::{diff_schema, save_compressed, VecOrStringLayout}; +use savefile_abi::AbiConnection; use smallvec::alloc::collections::BTreeMap; use std::borrow::Cow; use std::collections::HashSet; @@ -1546,14 +1547,14 @@ fn test_quickcheck_schema_roundtrip(a: Schema) -> bool { true } -#[ignore] +#[cfg(not(debug_assertions))] #[quickcheck] #[cfg(not(miri))] fn test_quickcheck_schema_diff_different(a: Schema, b: Schema) -> bool { _ = diff_schema(&a, &b, "".into()); //Check this doesn't crash true } -#[ignore] +#[cfg(not(debug_assertions))] #[quickcheck] #[cfg(not(miri))] fn test_quickcheck_schema_diff_same(a: Schema) -> bool { @@ -1609,3 +1610,25 @@ pub enum GoodEnum { fn test_good_enum() { assert!(unsafe { GoodEnum::repr_c_optimization_safe(0) }.is_yes()) } + +#[savefile_abi_exportable(version = 0)] +pub trait TestAddInterface { + fn simple_add(&self, x: u32, y: u32) -> u32; +} + +pub fn simple_add_call(conn: &AbiConnection, x: u32, y: u32) -> u32 { + (*conn).simple_add(x, y) +} +pub struct TestAddInterfaceImpl {} +impl TestAddInterface for TestAddInterfaceImpl { + fn simple_add(&self, x: u32, y: u32) -> u32 { + x + y + } +} +#[test] +fn dummy_test() { + let boxed: Box = Box::new(TestAddInterfaceImpl {}); + let conn = AbiConnection::from_boxed_trait(boxed).unwrap(); + let r = simple_add_call(&conn, 1, 2); + assert_eq!(r, 3); +} diff --git a/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs b/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs index e30c6b8..52f7102 100644 --- a/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs +++ b/savefile-test/src/savefile_abi_test/advanced_datatypes_test.rs @@ -13,8 +13,9 @@ pub trait AdvancedTestInterface { fn return_trait_object(&self) -> Box; - fn return_boxed_closure(&self) -> Box ()>; - fn many_callbacks(&mut self, x: &mut dyn FnMut(&dyn Fn(&dyn Fn() -> u32 )->u32) -> u32) -> u32; + fn return_boxed_closure(&self) -> Box u32>; + fn return_boxed_closure2(&self) -> Box; + fn many_callbacks(&mut self, x: &mut dyn FnMut(&dyn Fn(&dyn Fn() -> u32) -> u32) -> u32) -> u32; } struct SimpleImpl; @@ -44,15 +45,15 @@ impl AdvancedTestInterface for AdvancedTestInterfaceImpl { Box::new(SimpleImpl) } - fn return_boxed_closure(&self) -> Box { + fn return_boxed_closure(&self) -> Box u32> { + Box::new(|| 42) + } + fn return_boxed_closure2(&self) -> Box { Box::new(|| {}) } - fn many_callbacks(&mut self, x: &mut dyn FnMut(&dyn Fn(&dyn Fn() -> u32 )->u32) -> u32) -> u32 - { - x(&|y|{ - y() - }) + fn many_callbacks(&mut self, x: &mut dyn FnMut(&dyn Fn(&dyn Fn() -> u32) -> u32) -> u32) -> u32 { + x(&|y| y()) } } @@ -62,21 +63,38 @@ fn test_trait_object_in_return_position() { let conn = AbiConnection::from_boxed_trait(boxed).unwrap(); let ret = conn.return_trait_object(); - assert_eq!( ret.do_call(42), 42); - assert_eq!( ret.do_call(42), 42); + assert_eq!(ret.do_call(42), 42); + assert_eq!(ret.do_call(42), 42); +} +#[test] +fn test_return_boxed_closure() { + let closure; + let closure2; + { + let boxed: Box = Box::new(AdvancedTestInterfaceImpl {}); + let conn = AbiConnection::from_boxed_trait(boxed).unwrap(); + closure = conn.return_boxed_closure(); + closure2 = conn.return_boxed_closure2(); + assert_eq!(closure(), 42); + } + assert_eq!(closure(), 42); + closure2(); } #[test] fn test_call_many_callbacks() { let boxed: Box = Box::new(AdvancedTestInterfaceImpl {}); let mut conn = AbiConnection::from_boxed_trait(boxed).unwrap(); - assert_eq!(conn.many_callbacks(&mut |x|{ - x(&||{ - println!("In the inner sanctum!"); - 42 - }) - }), 42); + assert_eq!( + conn.many_callbacks(&mut |x| { + x(&|| { + println!("In the inner sanctum!"); + 42 + }) + }), + 42 + ); } #[test] fn test_advanced_abi2() { diff --git a/savefile-test/src/savefile_abi_test/basic_abi_tests.rs b/savefile-test/src/savefile_abi_test/basic_abi_tests.rs index 4f0cfc1..85b97d6 100644 --- a/savefile-test/src/savefile_abi_test/basic_abi_tests.rs +++ b/savefile-test/src/savefile_abi_test/basic_abi_tests.rs @@ -35,6 +35,8 @@ pub trait TestInterface { fn tuple_add3(&self, a: (u32, u32, u32), b: (u32, u32, u32)) -> (u32, u32, u32); fn boxes(&self, a: Box) -> Box; + + fn get_static_str(&self) -> &'static str; } #[derive(Default)] @@ -100,8 +102,8 @@ impl TestInterface for TestInterfaceImpl { fn string_arrays_add(&self, a: &[String], b: &[String]) -> Vec { let mut ret = vec![]; - for (a1,b1) in a.iter().zip(b.iter()) { - ret.push(a1.to_string() + b1); + for (a1, b1) in a.iter().zip(b.iter()) { + ret.push(a1.to_string() + b1); } ret } @@ -112,6 +114,10 @@ impl TestInterface for TestInterfaceImpl { fn count_chars_str(&self, x: &str) -> usize { x.len() } + + fn get_static_str(&self) -> &'static str { + "hello world" + } } savefile_abi_export!(TestInterfaceImpl, TestInterface); @@ -133,7 +139,7 @@ fn test_basic_call_abi() { assert_eq!(conn.count_chars(&"hejsan".to_string()), 6); assert_eq!(conn.count_chars_str("hejsan"), 6); - + assert_eq!(conn.get_static_str(), "hello world"); } #[test] @@ -146,7 +152,6 @@ fn test_slices() { let t = conn.string_arrays_add(&["hello ".to_string()], &["world".to_string()]); assert_eq!(t, vec!["hello world"]); - } #[test] @@ -203,7 +208,6 @@ fn test_abi_removed_with_custom_default() { assert_eq!(roundtripped, 42); } - #[cfg(feature = "nightly")] #[cfg(not(miri))] #[bench] @@ -214,8 +218,6 @@ fn bench_simple_call(b: &mut Bencher) { b.iter(move || conn.do_nothing()) } - - #[cfg(feature = "nightly")] #[cfg(not(miri))] #[bench] diff --git a/savefile.sublime-project b/savefile.sublime-project deleted file mode 100644 index 24db303..0000000 --- a/savefile.sublime-project +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": - [ - { - "path": "." - } - ] -} diff --git a/savefile/CHANGELOG.md b/savefile/CHANGELOG.md index 9e61002..3fe8e66 100644 --- a/savefile/CHANGELOG.md +++ b/savefile/CHANGELOG.md @@ -6,11 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.17.0-beta.13](https://github.com/avl/savefile/compare/savefile-v0.17.0-beta.12...savefile-v0.17.0-beta.13) - 2024-04-27 + +### Added +- savefile-abi2 + +These CHANGELOG-files are experimental. + ## [0.17.0-beta.12](https://github.com/avl/savefile/compare/savefile-v0.17.0-beta.11...savefile-v0.17.0-beta.12) - 2024-04-27 ### Other -- miri passes -- wip -- wip -- Work on safety -- Fix formatting +- Unspecified work \ No newline at end of file diff --git a/savefile/Cargo.toml b/savefile/Cargo.toml index 2ca5392..e83d164 100644 --- a/savefile/Cargo.toml +++ b/savefile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "savefile" -version = "0.17.0-beta.12" +version = "0.17.0-beta.13" authors = ["Anders Musikka "] documentation = "https://docs.rs/savefile/" homepage = "https://github.com/avl/savefile/" @@ -54,13 +54,13 @@ bit-set = {version = "0.5", optional = true} rustc-hash = {version = "1.1", optional = true} memoffset = "0.9" byteorder = "1.4" -savefile-derive = {path="../savefile-derive", version = "=0.17.0-beta.12", optional = true } +savefile-derive = {path="../savefile-derive", version = "=0.17.0-beta.13", optional = true } serde_derive = {version= "1.0", optional = true} serde = {version= "1.0", optional = true} quickcheck = {version= "1.0", optional = true} [dev-dependencies] -savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.12" } +savefile-derive = { path="../savefile-derive", version = "=0.17.0-beta.13" } [build-dependencies] rustc_version="0.2" diff --git a/savefile/src/lib.rs b/savefile/src/lib.rs index deac7ce..c676036 100644 --- a/savefile/src/lib.rs +++ b/savefile/src/lib.rs @@ -1019,7 +1019,7 @@ pub enum SavefileError { impl From for SavefileError { fn from(value: Utf8Error) -> Self { SavefileError::InvalidUtf8 { - msg: format!("{:?}", value) + msg: format!("{:?}", value), } } } @@ -2374,13 +2374,12 @@ pub struct Field { } impl Field { - /// Create a new instance of field, with the given name and type pub fn new(name: String, value: Box) -> Field { Field { name, value, - offset: None + offset: None, } } /// Create a new instance of field, with the given name and type. @@ -2388,12 +2387,8 @@ impl Field { /// /// # Safety /// The offset *must* be the correct offset of the field within its struct. - pub unsafe fn unsafe_new(name: String, value: Box, offset: Option) -> Field { - Field { - name, - value, - offset - } + pub unsafe fn unsafe_new(name: String, value: Box, offset: Option) -> Field { + Field { name, value, offset } } /// Determine if the two fields are laid out identically in memory, in their parent objects. pub fn layout_compatible(&self, other: &Field) -> bool { @@ -2465,9 +2460,10 @@ impl SchemaStruct { /// * fields: The fields of the struct pub fn new(dbg_name: String, fields: Vec) -> SchemaStruct { SchemaStruct { - dbg_name, fields, + dbg_name, + fields, size: None, - alignment: None + alignment: None, } } /// * dbg_name: The name of the struct @@ -2476,15 +2472,20 @@ impl SchemaStruct { /// Otherwise, the size of the struct in memory (std::mem::size_of::()). /// * alignment: If None, the memory layout of the struct is unspecified. /// Otherwise, the alignment of the struct (std::mem::align_of::()). - pub fn new_unsafe(dbg_name: String, fields: Vec, size: Option, alignment: Option) -> SchemaStruct { + pub fn new_unsafe( + dbg_name: String, + fields: Vec, + size: Option, + alignment: Option, + ) -> SchemaStruct { SchemaStruct { - dbg_name, fields, + dbg_name, + fields, size, - alignment + alignment, } } - fn layout_compatible(&self, other: &SchemaStruct) -> bool { if self.fields.len() != other.fields.len() { return false; @@ -2600,7 +2601,7 @@ impl SchemaEnum { discriminant_size, has_explicit_repr: false, size: None, - alignment: None + alignment: None, } } /// Create a new SchemaEnum instance. @@ -2621,8 +2622,14 @@ impl SchemaEnum { /// # Safety /// The argument 'has_explicit_repr' must only be true if the enum in fact has a #[repr(uX)] attribute. /// The size and alignment must be correct for the type. - pub fn new_unsafe(dbg_name: String, variants: Vec, discriminant_size: u8, - has_explicit_repr: bool, size: Option, alignment: Option) -> SchemaEnum { + pub fn new_unsafe( + dbg_name: String, + variants: Vec, + discriminant_size: u8, + has_explicit_repr: bool, + size: Option, + alignment: Option, + ) -> SchemaEnum { SchemaEnum { dbg_name, variants, @@ -2708,7 +2715,6 @@ pub enum SchemaPrimitive { } impl SchemaPrimitive { fn layout_compatible(&self, other: &SchemaPrimitive) -> bool { - if let (SchemaPrimitive::schema_string(layout1), SchemaPrimitive::schema_string(layout2)) = (self, other) { if *layout1 == VecOrStringLayout::Unknown || *layout2 == VecOrStringLayout::Unknown { return false; @@ -3087,18 +3093,16 @@ impl Schema { Schema::Custom(_) => "custom", Schema::Boxed(_) => "box", Schema::FnClosure(_, _) => "fntrait", - Schema::Slice(_) => {"slice"} - Schema::Str => {"str"} - Schema::Reference(_) => {"reference"} - Schema::Trait(_, _) => {"trait"} + Schema::Slice(_) => "slice", + Schema::Str => "str", + Schema::Reference(_) => "reference", + Schema::Trait(_, _) => "trait", } } /// Determine if the two fields are laid out identically in memory, in their parent objects. pub fn layout_compatible(&self, b_native: &Schema) -> bool { match (self, b_native) { - (Schema::Struct(a), Schema::Struct(b)) => { - a.layout_compatible(b) - }, + (Schema::Struct(a), Schema::Struct(b)) => a.layout_compatible(b), (Schema::Enum(a), Schema::Enum(b)) => a.layout_compatible(b), (Schema::Primitive(a), Schema::Primitive(b)) => a.layout_compatible(b), (Schema::Vector(a, a_standard_layout), Schema::Vector(b, b_standard_layout)) => { @@ -3125,9 +3129,7 @@ impl Schema { // if boxed traits are contained in a data structure false } - (Schema::Reference(a), Schema::Reference(b)) => { - a.layout_compatible(&*b) - } + (Schema::Reference(a), Schema::Reference(b)) => a.layout_compatible(&*b), _ => false, } } @@ -3150,8 +3152,8 @@ impl Schema { pub fn new_tuple2(version: u32) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "2-Tuple".to_string(), - size: Some(std::mem::size_of::<(T1,T2)>()), - alignment: Some(std::mem::align_of::<(T1,T2)>()), + size: Some(std::mem::size_of::<(T1, T2)>()), + alignment: Some(std::mem::align_of::<(T1, T2)>()), fields: vec![ Field { name: "0".to_string(), @@ -3170,8 +3172,8 @@ impl Schema { pub fn new_tuple3(version: u32) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "3-Tuple".to_string(), - size: Some(std::mem::size_of::<(T1,T2,T3)>()), - alignment: Some(std::mem::align_of::<(T1,T2,T3)>()), + size: Some(std::mem::size_of::<(T1, T2, T3)>()), + alignment: Some(std::mem::align_of::<(T1, T2, T3)>()), fields: vec![ Field { name: "0".to_string(), @@ -3195,8 +3197,8 @@ impl Schema { pub fn new_tuple4(version: u32) -> Schema { Schema::Struct(SchemaStruct { dbg_name: "4-Tuple".to_string(), - size: Some(std::mem::size_of::<(T1,T2,T3,T4)>()), - alignment: Some(std::mem::align_of::<(T1,T2,T3,T4)>()), + size: Some(std::mem::size_of::<(T1, T2, T3, T4)>()), + alignment: Some(std::mem::align_of::<(T1, T2, T3, T4)>()), fields: vec![ Field { name: "0".to_string(), @@ -3379,23 +3381,18 @@ pub fn diff_schema(a: &Schema, b: &Schema, path: String) -> Option { return None; } (Schema::Str, Schema::Str) => { - return None; } (Schema::Boxed(a), Schema::Boxed(b)) => { - return diff_schema(&**a, &**b, path); } (Schema::Reference(a), Schema::Reference(b)) => { - return diff_schema(&**a, &**b, path); } (Schema::Slice(a), Schema::Slice(b)) => { - return diff_schema(&**a, &**b, path); } - (Schema::Trait(amut, a), Schema::Trait(bmut, b)) | - (Schema::FnClosure(amut, a), Schema::FnClosure(bmut, b)) => { + (Schema::Trait(amut, a), Schema::Trait(bmut, b)) | (Schema::FnClosure(amut, a), Schema::FnClosure(bmut, b)) => { if amut != bmut { if *amut { return Some(format!( @@ -3410,7 +3407,7 @@ pub fn diff_schema(a: &Schema, b: &Schema, path: String) -> Option { )); } } - return diff_abi_def(a,b, path); + return diff_abi_def(a, b, path); } (a, b) => (a.top_level_description(), b.top_level_description()), }; @@ -3433,9 +3430,7 @@ fn diff_abi_def(a: &AbiTraitDefinition, b: &AbiTraitDefinition, path: String) -> bmet.info.arguments.len() )); } - for (arg_index, (a_arg, b_arg)) in - amet.info.arguments.iter().zip(bmet.info.arguments.iter()).enumerate() - { + for (arg_index, (a_arg, b_arg)) in amet.info.arguments.iter().zip(bmet.info.arguments.iter()).enumerate() { if let Some(diff) = diff_schema( &a_arg.schema, &b_arg.schema, @@ -3811,8 +3806,8 @@ impl Arbitrary for SchemaStruct { .map(|_| <_ as Arbitrary>::arbitrary(g)) .collect(), dbg_name: <_ as Arbitrary>::arbitrary(g), - size: <_ as Arbitrary>::arbitrary(g), - alignment: <_ as Arbitrary>::arbitrary(g), + size: <_ as Arbitrary>::arbitrary(g), + alignment: <_ as Arbitrary>::arbitrary(g), } } } @@ -3955,19 +3950,13 @@ impl Deserialize for Schema { <_ as Deserialize>::deserialize(deserializer)?, <_ as Deserialize>::deserialize(deserializer)?, ), - 12 => { - Schema::Slice(Box::new(<_ as Deserialize>::deserialize(deserializer)?)) - } + 12 => Schema::Slice(Box::new(<_ as Deserialize>::deserialize(deserializer)?)), 13 => Schema::Str, - 14 => { - Schema::Reference(Box::new(<_ as Deserialize>::deserialize(deserializer)?)) - } - 15 => { - Schema::Trait( - <_ as Deserialize>::deserialize(deserializer)?, - <_ as Deserialize>::deserialize(deserializer)? - ) - } + 14 => Schema::Reference(Box::new(<_ as Deserialize>::deserialize(deserializer)?)), + 15 => Schema::Trait( + <_ as Deserialize>::deserialize(deserializer)?, + <_ as Deserialize>::deserialize(deserializer)?, + ), c => { return Err(SavefileError::GeneralError { msg: format!("Corrupt schema, schema variant {} encountered", c), @@ -5562,8 +5551,8 @@ pub const fn calculate_slice_memory_layout() -> VecOrStringLayout { if std::mem::size_of::<&[T]>() != 16 || std::mem::size_of::() != 16 { VecOrStringLayout::Unknown } else { - let test_slice:&[T] = &[]; - let insp: RawSliceInspector = unsafe { std::mem::transmute_copy::<&[T],RawSliceInspector>(&test_slice) }; + let test_slice: &[T] = &[]; + let insp: RawSliceInspector = unsafe { std::mem::transmute_copy::<&[T], RawSliceInspector>(&test_slice) }; insp.get_layout() } } @@ -6260,7 +6249,7 @@ use std::convert::{TryFrom, TryInto}; use std::fmt::{Debug, Display, Formatter}; use std::marker::PhantomData; use std::path::{Path, PathBuf}; -use std::ptr::{NonNull}; +use std::ptr::NonNull; use std::slice; use std::sync::Arc;