diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-02-02 23:56:36 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-02-02 23:56:36 +0000 |
commit | 10ffb6bbcf05dc0a71bbf29006b34c8a80d15bd0 (patch) | |
tree | 906f9dd3bae52323bc6fde373383247e3f67f65a | |
parent | eaa766f14935c0f26afc7f8225143d1dc261b6d7 (diff) | |
parent | adfecb30ba9dc20ed624a80576a1a5aaad967491 (diff) | |
download | zerocopy-simpleperf-release.tar.gz |
Snap for 11400057 from adfecb30ba9dc20ed624a80576a1a5aaad967491 to simpleperf-releasesimpleperf-release
Change-Id: I0297460faebc208229f3940b3fe7ee5178e79d00
299 files changed, 15456 insertions, 1083 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index e74a0f5..80e9fc6 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "c150d4f1b75fc21240574b6b7dbbcdc236d388b0" + "sha1": "5b76223e0eddea87e0f81429e218d12de94cd954" }, "path_in_vcs": "" }
\ No newline at end of file @@ -23,9 +23,9 @@ rust_library { host_supported: true, crate_name: "zerocopy", cargo_env_compat: true, - cargo_pkg_version: "0.7.5", + cargo_pkg_version: "0.7.29", srcs: ["src/lib.rs"], - edition: "2021", + edition: "2018", features: [ "byteorder", "derive", @@ -46,9 +46,9 @@ rust_library_rlib { name: "libzerocopy_nostd", crate_name: "zerocopy", cargo_env_compat: true, - cargo_pkg_version: "0.7.5", + cargo_pkg_version: "0.7.29", srcs: ["src/lib.rs"], - edition: "2021", + edition: "2018", features: [ "alloc", "byteorder", @@ -77,9 +77,9 @@ rust_library_rlib { name: "libzerocopy_nostd_noalloc", crate_name: "zerocopy", cargo_env_compat: true, - cargo_pkg_version: "0.7.5", + cargo_pkg_version: "0.7.29", srcs: ["src/lib.rs"], - edition: "2021", + edition: "2018", features: [ "byteorder", "derive", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d79e93..929e809 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,10 @@ -<!-- Copyright 2022 The Fuchsia Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. --> +<!-- Copyright 2022 The Fuchsia Authors + +Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +This file may not be copied, modified, or distributed except according to +those terms. --> # How to Contribute @@ -10,23 +10,31 @@ # See Cargo.toml.orig for the original contents. [package] -edition = "2021" -rust-version = "1.61.0" +edition = "2018" +rust-version = "1.60.0" name = "zerocopy" -version = "0.7.5" +version = "0.7.29" authors = ["Joshua Liebow-Feeser <joshlf@google.com>"] exclude = [".*"] description = "Utilities for zero-copy parsing and serialization" readme = "README.md" -license = "BSD-2-Clause" +license = "BSD-2-Clause OR Apache-2.0 OR MIT" repository = "https://github.com/google/zerocopy" [package.metadata.ci] -pinned-nightly = "nightly-2023-05-25" -pinned-stable = "1.69.0" +pinned-nightly = "nightly-2023-12-04" +pinned-stable = "1.74.0" [package.metadata.docs.rs] all-features = true +rustdoc-args = [ + "--cfg", + "doc_cfg", + "--generate-link-to-definition", +] + +[package.metadata.playground] +features = ["__internal_use_only_features_that_work_on_stable"] [dependencies.byteorder] version = "1.3" @@ -34,12 +42,15 @@ optional = true default-features = false [dependencies.zerocopy-derive] -version = "=0.7.5" +version = "=0.7.29" optional = true [dev-dependencies.assert_matches] version = "1.5" +[dev-dependencies.elain] +version = "0.3.0" + [dev-dependencies.itertools] version = "0.11" @@ -54,10 +65,11 @@ version = "1.0" version = "1.1" [dev-dependencies.trybuild] -version = "=1.0.80" +version = "=1.0.85" +features = ["diff"] [dev-dependencies.zerocopy-derive] -version = "=0.7.5" +version = "=0.7.29" [features] __internal_use_only_features_that_work_on_stable = [ @@ -72,4 +84,4 @@ simd = [] simd-nightly = ["simd"] [target."cfg(any())".dependencies.zerocopy-derive] -version = "=0.7.5" +version = "=0.7.29" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index aa05c44..9355f08 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,10 @@ -# Copyright 2018 The Fuchsia Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. +# Copyright 2018 The Fuchsia Authors +# +# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. # Put both crates in a single workspace so that `trybuild` compiler errors have # paths that are stable regardless of the path to the repository root. This @@ -9,24 +13,28 @@ [workspace] [package] -edition = "2021" +edition = "2018" name = "zerocopy" -version = "0.7.5" +version = "0.7.29" authors = ["Joshua Liebow-Feeser <joshlf@google.com>"] description = "Utilities for zero-copy parsing and serialization" -license = "BSD-2-Clause" +license = "BSD-2-Clause OR Apache-2.0 OR MIT" repository = "https://github.com/google/zerocopy" -rust-version = "1.61.0" +rust-version = "1.60.0" exclude = [".*"] [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] [package.metadata.ci] # The versions of the stable and nightly compiler toolchains to use in CI. -pinned-stable = "1.69.0" -pinned-nightly = "nightly-2023-05-25" +pinned-stable = "1.74.0" +pinned-nightly = "nightly-2023-12-04" + +[package.metadata.playground] +features = ["__internal_use_only_features_that_work_on_stable"] [features] default = ["byteorder"] @@ -41,7 +49,7 @@ simd-nightly = ["simd"] __internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd"] [dependencies] -zerocopy-derive = { version = "=0.7.5", path = "zerocopy-derive", optional = true } +zerocopy-derive = { version = "=0.7.29", path = "zerocopy-derive", optional = true } [dependencies.byteorder] version = "1.3" @@ -52,7 +60,7 @@ optional = true # zerocopy-derive remain equal, even if the 'derive' feature isn't used. # See: https://github.com/matklad/macro-dep-test [target.'cfg(any())'.dependencies] -zerocopy-derive = { version = "=0.7.5", path = "zerocopy-derive" } +zerocopy-derive = { version = "=0.7.29", path = "zerocopy-derive" } [dev-dependencies] assert_matches = "1.5" @@ -60,10 +68,13 @@ itertools = "0.11" rand = { version = "0.8.5", features = ["small_rng"] } rustversion = "1.0" static_assertions = "1.1" +testutil = { path = "testutil" } # Pinned to a specific version so that the version used for local development # and the version used in CI are guaranteed to be the same. Future versions # sometimes change the output format slightly, so a version mismatch can cause # CI test failures. -trybuild = "=1.0.80" +trybuild = { version = "=1.0.85", features = ["diff"] } # In tests, unlike in production, zerocopy-derive is not optional -zerocopy-derive = { version = "=0.7.5", path = "zerocopy-derive" } +zerocopy-derive = { version = "=0.7.29", path = "zerocopy-derive" } +# TODO(#381) Remove this dependency once we have our own layout gadgets. +elain = "0.3.0" diff --git a/INTERNAL.md b/INTERNAL.md index 4c9ed6a..4e7f440 100644 --- a/INTERNAL.md +++ b/INTERNAL.md @@ -1,3 +1,11 @@ +<!-- Copyright 2022 The Fuchsia Authors + +Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +This file may not be copied, modified, or distributed except according to +those terms. --> + # Internal details This file documents various internal details of zerocopy and its infrastructure @@ -18,8 +26,11 @@ have a working Miri, so we need to pin to one that does (see https://rust-lang.github.io/rustup-components-history/). Updating the versions pinned in CI may cause the UI tests to break. In order to -fix UI tests after a version update, set the environment variable -`TRYBUILD=overwrite` while running `cargo test`. +fix UI tests after a version update, run: + +``` +$ TRYBUILD=overwrite ./cargo.sh +all test +``` ## Crate versions @@ -30,4 +41,4 @@ when published on crates.io, both crates effectively constitute a single atomic version. So long as the code in zerocopy is compatible with the code in zerocopy-derive in the same Git commit, then publishing them both is fine. This frees us from the normal task of reasoning about compatibility with a range of -semver-compatible versions of different crates.
\ No newline at end of file +semver-compatible versions of different crates. diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..2dc22c1 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 The Fuchsia Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/LICENSE-BSD b/LICENSE-BSD new file mode 100644 index 0000000..7ed244f --- /dev/null +++ b/LICENSE-BSD @@ -0,0 +1,24 @@ +Copyright 2019 The Fuchsia Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..26e1521 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright 2023 The Fuchsia Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + @@ -5,19 +5,20 @@ name: "zerocopy" description: "Utilities for zero-copy parsing and serialization" third_party { - url { - type: HOMEPAGE - value: "https://crates.io/crates/zerocopy" - } - url { - type: ARCHIVE - value: "https://static.crates.io/crates/zerocopy/zerocopy-0.7.5.crate" - } - version: "0.7.5" license_type: NOTICE last_upgrade_date { year: 2023 - month: 9 - day: 28 + month: 12 + day: 5 + } + identifier { + type: "crates.io" + value: "https://crates.io/crates/zerocopy" + version: "" + } + identifier { + type: "Archive" + value: "https://static.crates.io/crates/zerocopy/zerocopy-0.7.29.crate" + version: "0.7.29" } } diff --git a/POLICIES.md b/POLICIES.md new file mode 100644 index 0000000..a2f175c --- /dev/null +++ b/POLICIES.md @@ -0,0 +1,95 @@ +<!-- Copyright 2023 The Fuchsia Authors + +Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +This file may not be copied, modified, or distributed except according to +those terms. --> + +# Zerocopy's Policies + +## Soundness + +Zerocopy is expressly designed for use in security-critical contexts. It is used +in hardware security firmware, cryptographic implementations, hypervisors, and +more. We understand that software in these contexts has a very high bar for +correctness, and we take our responsibility to meet that bar very seriously. + +This section describes policies which are designed to ensure the correctness and +soundness of our code and prevent regressions. + +### Forwards-compatibility + +Rust does not currently have a formal memory model. As such, while Rust provides +guarantees about the semantics of some operations, the semantics of many +operations is up in the air and subject to change. + +Zerocopy strives to ensure that our code - and code emitted by our custom +derives - is sound under any version of Rust as early as our MSRV, and will +continue to be sound under any future version of Rust. The policies in this +section are designed to help ensure that we live up to this goal. + +### Safety comments + +Each non-test `unsafe` block must be annotated with a "safety comment" which +provides a rationale for its soundness. In order to ensure that our soundness is +forwards-compatible, safety comments must satisfy the following criteria: +- Safety comments must constitute a (possibly informal) proof that all of Rust's + soundness rules are upheld. +- Safety comments must only rely for their correctness on statements which + appear in the stable versions of the [Rust Reference] or standard library + documentation (ie, the docs for [core], [alloc], and [std]); arguments which + rely on text from the beta or nightly versions of these documents are not + considered complete. +- All statements from the Reference or standard library documentation which are + relied upon for soundness must be quoted in the safety comment. This ensures + that there is no ambiguity as to what aspect of the text is being cited. This + is especially important in cases where the text of these documents changes in + the future. Such changes are of course required to be backwards-compatible, + but may change the manner in which a particular guarantee is explained. + +We use the [`clippy::undocumented_unsafe_blocks`] lint to ensure that `unsafe` +blocks cannot be added without a safety comment. Note that there are a few +outstanding uncommented `unsafe` blocks which are tracked in [#429]. Our goal is +to reach 100% safety comment coverage and not regress once we've reached it. + +[Rust Reference]: https://doc.rust-lang.org/reference/ +[core]: https://doc.rust-lang.org/stable/core/ +[alloc]: https://doc.rust-lang.org/stable/alloc/ +[std]: https://doc.rust-lang.org/stable/std/ +[`clippy::undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#/undocumented_unsafe_blocks +[#429]: https://github.com/google/zerocopy/issues/429 + +#### Exceptions to our safety comment policy + +In rare circumstances, the soundness of an `unsafe` block may depend upon +semantics which are widely agreed upon but not formally guaranteed. In order to +avoid slowing down zerocopy's development to an unreasonable degree, a safety +comment may violate our safety comment policy so long as all of the following +hold: +- The safety comment's correctness may rely on semantics which are not + guaranteed in official Rust documentation *so long as* a member of the Rust + team has articulated in an official communication (e.g. a comment on a Rust + GitHub repo) that Rust intends to guarantee particular semantics. +- There exists an active effort to formalize the guarantee in Rust's official + documentation. + +### Target architecture support + +Zerocopy bases its soundness on guarantees made about the semantics of Rust +which appear in the Rust Reference or standard library documentation; zerocopy +is sound so long as these guarantees hold. There are known cases in which these +guarantees do not hold on certain target architectures (see +[rust-lang/unsafe-code-guidelines#461]); on such target architectures, zerocopy +may be unsound. We consider it outside of zerocopy's scope to reason about these +cases. Zerocopy makes no effort maintain soundness in cases where Rust's +documented guarantees do not hold. + +[rust-lang/unsafe-code-guidelines#461]: https://github.com/rust-lang/unsafe-code-guidelines/issues/461 + +## MSRV + +Our minimum supported Rust version (MSRV) is encoded in our `Cargo.toml` file. +We consider an increase in MSRV to be a semver-breaking change, and will only +increase our MSRV during semver-breaking version changes (e.g., 0.1 -> 0.2, 1.0 +-> 2.0, etc). @@ -1,6 +1,10 @@ -<!-- Copyright 2022 The Fuchsia Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. +<!-- Copyright 2022 The Fuchsia Authors + +Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +This file may not be copied, modified, or distributed except according to +those terms. WARNING: DO NOT EDIT THIS FILE. It is generated automatically. Edits should be made in the doc comment on `src/lib.rs` or in `generate-readme.sh`. @@ -86,6 +90,59 @@ for network parsing. [simd-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html +## Security Ethos + +Zerocopy is expressly designed for use in security-critical contexts. We +strive to ensure that that zerocopy code is sound under Rust's current +memory model, and *any future memory model*. We ensure this by: +- **...not 'guessing' about Rust's semantics.** + We annotate `unsafe` code with a precise rationale for its soundness that + cites a relevant section of Rust's official documentation. When Rust's + documented semantics are unclear, we work with the Rust Operational + Semantics Team to clarify Rust's documentation. +- **...rigorously testing our implementation.** + We run tests using [Miri], ensuring that zerocopy is sound across a wide + array of supported target platforms of varying endianness and pointer + width, and across both current and experimental memory models of Rust. +- **...formally proving the correctness of our implementation.** + We apply formal verification tools like [Kani][kani] to prove zerocopy's + correctness. + +For more information, see our full [soundness policy]. + +[Miri]: https://github.com/rust-lang/miri +[Kani]: https://github.com/model-checking/kani +[soundness policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#soundness + +## Relationship to Project Safe Transmute + +[Project Safe Transmute] is an official initiative of the Rust Project to +develop language-level support for safer transmutation. The Project consults +with crates like zerocopy to identify aspects of safer transmutation that +would benefit from compiler support, and has developed an [experimental, +compiler-supported analysis][mcp-transmutability] which determines whether, +for a given type, any value of that type may be soundly transmuted into +another type. Once this functionality is sufficiently mature, zerocopy +intends to replace its internal transmutability analysis (implemented by our +custom derives) with the compiler-supported one. This change will likely be +an implementation detail that is invisible to zerocopy's users. + +Project Safe Transmute will not replace the need for most of zerocopy's +higher-level abstractions. The experimental compiler analysis is a tool for +checking the soundness of `unsafe` code, not a tool to avoid writing +`unsafe` code altogether. For the foreseeable future, crates like zerocopy +will still be required in order to provide higher-level abstractions on top +of the building block provided by Project Safe Transmute. + +[Project Safe Transmute]: https://rust-lang.github.io/rfcs/2835-project-safe-transmute.html +[mcp-transmutability]: https://github.com/rust-lang/compiler-team/issues/411 + +## MSRV + +See our [MSRV policy]. + +[MSRV policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#msrv + ## Disclaimer Disclaimer: Zerocopy is not an officially supported Google product. @@ -1,8 +1,12 @@ #!/bin/bash # -# Copyright 2023 The Fuchsia Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. +# Copyright 2023 The Fuchsia Authors +# +# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. # This script is a thin wrapper around Cargo that provides human-friendly # toolchain names which are automatically translated to the toolchain versions @@ -33,7 +37,14 @@ function print-usage-and-exit { [[ $# -gt 0 ]] || print-usage-and-exit function pkg-meta { - cargo metadata --format-version 1 | jq -r ".packages[] | select(.name == \"zerocopy\").$1" + # NOTE(#547): We set `CARGO_TARGET_DIR` here because `cargo metadata` + # sometimes causes the `cargo-metadata` crate to be rebuilt from source using + # the default toolchain. This has the effect of clobbering any existing build + # artifacts from whatever toolchain the user has specified (e.g., `+nightly`), + # causing the subsequent `cargo` invocation to rebuild unnecessarily. By + # specifying a separate build directory here, we ensure that this never + # clobbers the build artifacts used by the later `cargo` invocation. + CARGO_TARGET_DIR=target/cargo-sh cargo metadata --format-version 1 | jq -r ".packages[] | select(.name == \"zerocopy\").$1" } function lookup-version { @@ -55,6 +66,23 @@ function lookup-version { esac } +function get-rustflags { + [ "$1" == nightly ] && echo "--cfg __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS" +} + +function prompt { + PROMPT="$1" + YES="$2" + while true; do + read -p "$PROMPT " yn + case "$yn" in + [Yy]) $YES; return $?; ;; + [Nn]) return 1; ;; + *) break; ;; + esac + done +} + case "$1" in # cargo.sh --version <toolchain-name> --version) @@ -66,15 +94,25 @@ case "$1" in echo "[cargo.sh] warning: running the same command for each toolchain (msrv, stable, nightly)" >&2 for toolchain in msrv stable nightly; do echo "[cargo.sh] running with toolchain: $toolchain" >&2 - TOOLCHAIN="$(lookup-version $toolchain)" - cargo "+$TOOLCHAIN" ${@:2} + $0 "+$toolchain" ${@:2} done exit 0 ;; # cargo.sh +<toolchain-name> [...] +*) TOOLCHAIN="$(lookup-version ${1:1})" - cargo "+$TOOLCHAIN" ${@:2} + + cargo "+$TOOLCHAIN" version &>/dev/null && \ + rustup "+$TOOLCHAIN" component list | grep '^rust-src (installed)$' >/dev/null || { + echo "[cargo.sh] missing either toolchain '$TOOLCHAIN' or component 'rust-src'" >&2 + # If we're running in a GitHub action, then it's better to bail than to + # hang waiting for input we're never going to get. + [ -z ${GITHUB_RUN_ID+x} ] || exit 1 + prompt "[cargo.sh] would you like to install toolchain '$TOOLCHAIN' and component 'rust-src' via 'rustup'?" \ + "rustup toolchain install $TOOLCHAIN -c rust-src" + } || exit 1 + + RUSTFLAGS="$(get-rustflags ${1:1}) $RUSTFLAGS" cargo "+$TOOLCHAIN" ${@:2} ;; *) print-usage-and-exit diff --git a/cargo2rulesmk.json b/cargo2rulesmk.json new file mode 100644 index 0000000..a9e982a --- /dev/null +++ b/cargo2rulesmk.json @@ -0,0 +1,3 @@ +{ + "features": "default,derive,alloc" +} diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..9c11406 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,10 @@ +# Copyright 2023 The Fuchsia Authors +# +# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +accept-comment-above-statement = true +accept-comment-above-attributes = true diff --git a/generate-readme.sh b/generate-readme.sh index b900737..be0dc92 100755 --- a/generate-readme.sh +++ b/generate-readme.sh @@ -1,8 +1,12 @@ #!/bin/bash # -# Copyright 2022 The Fuchsia Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. +# Copyright 2022 The Fuchsia Authors +# +# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. set -eo pipefail @@ -11,9 +15,13 @@ BODY=$(mktemp) DISCLAIMER_FOOTER=$(mktemp) cat > $COPYRIGHT_HEADER <<'EOF' -<!-- Copyright 2022 The Fuchsia Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. +<!-- Copyright 2022 The Fuchsia Authors + +Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +This file may not be copied, modified, or distributed except according to +those terms. WARNING: DO NOT EDIT THIS FILE. It is generated automatically. Edits should be made in the doc comment on `src/lib.rs` or in `generate-readme.sh`. diff --git a/patches/rules.mk.diff b/patches/rules.mk.diff new file mode 100644 index 0000000..ecea77e --- /dev/null +++ b/patches/rules.mk.diff @@ -0,0 +1,14 @@ +diff --git a/rules.mk b/rules.mk +index 05e4383..9e71d6d 100644 +--- a/rules.mk ++++ b/rules.mk +@@ -8,8 +8,8 @@ MODULE_SRCS := \ + $(LOCAL_DIR)/src/lib.rs \ + + MODULE_RUST_EDITION := 2018 ++# TODO(b/316076550): Remove rules.mk patch when cargo2rulesmk.py is fixed + MODULE_RUSTFLAGS += \ +- --cfg 'feature="byteorder"' \ + --cfg 'feature="default"' \ + --cfg 'feature="derive"' \ + --cfg 'feature="zerocopy-derive"' \ diff --git a/rules.mk b/rules.mk new file mode 100644 index 0000000..959d61c --- /dev/null +++ b/rules.mk @@ -0,0 +1,23 @@ +# This file is generated by cargo2rulesmk.py --run --config cargo2rulesmk.json. +# Do not modify this file as changes will be overridden on upgrade. + +LOCAL_DIR := $(GET_LOCAL_DIR) +MODULE := $(LOCAL_DIR) +MODULE_CRATE_NAME := zerocopy +MODULE_SRCS := \ + $(LOCAL_DIR)/src/lib.rs \ + +MODULE_RUST_EDITION := 2018 +MODULE_RUSTFLAGS += \ + --cfg 'feature="alloc"' \ + --cfg 'feature="byteorder"' \ + --cfg 'feature="default"' \ + --cfg 'feature="derive"' \ + --cfg 'feature="zerocopy-derive"' \ + +MODULE_LIBRARY_DEPS := \ + $(call FIND_CRATE,liballoc) \ + external/rust/crates/byteorder \ + external/rust/crates/zerocopy-derive \ + +include make/library.mk diff --git a/rustfmt.toml b/rustfmt.toml index 0b0d02c..c967afe 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,6 +1,10 @@ -# Copyright 2022 The Fuchsia Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. +# Copyright 2022 The Fuchsia Authors +# +# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. edition = "2021" diff --git a/src/byteorder.rs b/src/byteorder.rs index ecee7a0..2769410 100644 --- a/src/byteorder.rs +++ b/src/byteorder.rs @@ -1,6 +1,10 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2019 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. //! Byte order-aware numeric primitives. //! @@ -93,13 +97,12 @@ macro_rules! impl_fmt_traits { impl_fmt_trait!($name, $native, Display); }; ($name:ident, $native:ident, "unsigned integer") => { - impl_fmt_traits!($name, $native, @all_traits); + impl_fmt_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, "signed integer") => { - impl_fmt_traits!($name, $native, @all_traits); + impl_fmt_traits!($name, $native, @all_types); }; - - ($name:ident, $native:ident, @all_traits) => { + ($name:ident, $native:ident, @all_types) => { impl_fmt_trait!($name, $native, Display); impl_fmt_trait!($name, $native, Octal); impl_fmt_trait!($name, $native, LowerHex); @@ -108,6 +111,101 @@ macro_rules! impl_fmt_traits { }; } +macro_rules! impl_ops_traits { + ($name:ident, $native:ident, "floating point number") => { + impl_ops_traits!($name, $native, @all_types); + impl_ops_traits!($name, $native, @signed_integer_floating_point); + }; + ($name:ident, $native:ident, "unsigned integer") => { + impl_ops_traits!($name, $native, @signed_unsigned_integer); + impl_ops_traits!($name, $native, @all_types); + }; + ($name:ident, $native:ident, "signed integer") => { + impl_ops_traits!($name, $native, @signed_unsigned_integer); + impl_ops_traits!($name, $native, @signed_integer_floating_point); + impl_ops_traits!($name, $native, @all_types); + }; + ($name:ident, $native:ident, @signed_unsigned_integer) => { + impl_ops_traits!(@without_byteorder_swap $name, $native, BitAnd, bitand, BitAndAssign, bitand_assign); + impl_ops_traits!(@without_byteorder_swap $name, $native, BitOr, bitor, BitOrAssign, bitor_assign); + impl_ops_traits!(@without_byteorder_swap $name, $native, BitXor, bitxor, BitXorAssign, bitxor_assign); + impl_ops_traits!(@with_byteorder_swap $name, $native, Shl, shl, ShlAssign, shl_assign); + impl_ops_traits!(@with_byteorder_swap $name, $native, Shr, shr, ShrAssign, shr_assign); + + impl<O> core::ops::Not for $name<O> { + type Output = $name<O>; + + #[inline(always)] + fn not(self) -> $name<O> { + let self_native = $native::from_ne_bytes(self.0); + $name((!self_native).to_ne_bytes(), PhantomData) + } + } + }; + ($name:ident, $native:ident, @signed_integer_floating_point) => { + impl<O: ByteOrder> core::ops::Neg for $name<O> { + type Output = $name<O>; + + #[inline(always)] + fn neg(self) -> $name<O> { + let self_native: $native = self.get(); + #[allow(clippy::arithmetic_side_effects)] + $name::<O>::new(-self_native) + } + } + }; + ($name:ident, $native:ident, @all_types) => { + impl_ops_traits!(@with_byteorder_swap $name, $native, Add, add, AddAssign, add_assign); + impl_ops_traits!(@with_byteorder_swap $name, $native, Div, div, DivAssign, div_assign); + impl_ops_traits!(@with_byteorder_swap $name, $native, Mul, mul, MulAssign, mul_assign); + impl_ops_traits!(@with_byteorder_swap $name, $native, Rem, rem, RemAssign, rem_assign); + impl_ops_traits!(@with_byteorder_swap $name, $native, Sub, sub, SubAssign, sub_assign); + }; + (@with_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { + impl<O: ByteOrder> core::ops::$trait for $name<O> { + type Output = $name<O>; + + #[inline(always)] + fn $method(self, rhs: $name<O>) -> $name<O> { + let self_native: $native = self.get(); + let rhs_native: $native = rhs.get(); + let result_native = core::ops::$trait::$method(self_native, rhs_native); + $name::<O>::new(result_native) + } + } + + impl<O: ByteOrder> core::ops::$trait_assign for $name<O> { + #[inline(always)] + fn $method_assign(&mut self, rhs: $name<O>) { + *self = core::ops::$trait::$method(*self, rhs); + } + } + }; + // Implement traits in terms of the same trait on the native type, but + // without performing a byte order swap. This only works for bitwise + // operations like `&`, `|`, etc. + (@without_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { + impl<O: ByteOrder> core::ops::$trait for $name<O> { + type Output = $name<O>; + + #[inline(always)] + fn $method(self, rhs: $name<O>) -> $name<O> { + let self_native = $native::from_ne_bytes(self.0); + let rhs_native = $native::from_ne_bytes(rhs.0); + let result_native = core::ops::$trait::$method(self_native, rhs_native); + $name(result_native.to_ne_bytes(), PhantomData) + } + } + + impl<O: ByteOrder> core::ops::$trait_assign for $name<O> { + #[inline(always)] + fn $method_assign(&mut self, rhs: $name<O>) { + *self = core::ops::$trait::$method(*self, rhs); + } + } + }; +} + macro_rules! doc_comment { ($x:expr, $($tt:tt)*) => { #[doc = $x] @@ -150,11 +248,13 @@ macro_rules! define_type { [$($larger_byteorder_try:ident),*]) => { doc_comment! { concat!("A ", stringify!($bits), "-bit ", $number_kind, - " stored in `O` byte order. + " stored in a given byte order. `", stringify!($name), "` is like the native `", stringify!($native), "` type with two major differences: First, it has no alignment requirement (its alignment is 1). -Second, the endianness of its memory layout is given by the type parameter `O`. +Second, the endianness of its memory layout is given by the type parameter `O`, +which can be any type which implements [`ByteOrder`]. In particular, this refers +to [`BigEndian`], [`LittleEndian`], [`NativeEndian`], and [`NetworkEndian`]. ", stringify!($article), " `", stringify!($name), "` can be constructed using the [`new`] method, and its contained value can be obtained as a native @@ -176,11 +276,14 @@ example of how it can be used for parsing UDP packets. [`AsBytes`]: crate::AsBytes [`Unaligned`]: crate::Unaligned"), #[derive(Copy, Clone, Eq, PartialEq, Hash)] - #[cfg_attr(any(feature = "derive", test), derive(FromZeroes, FromBytes, AsBytes, Unaligned))] + #[cfg_attr(any(feature = "derive", test), derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned))] #[repr(transparent)] pub struct $name<O>([u8; $bytes], PhantomData<O>); } + #[cfg(not(any(feature = "derive", test)))] + impl_known_layout!(O => $name<O>); + safety_comment! { /// SAFETY: /// `$name<O>` is `repr(transparent)`, and so it has the same layout @@ -347,6 +450,7 @@ example of how it can be used for parsing UDP packets. } impl_fmt_traits!($name, $native, $number_kind); + impl_ops_traits!($name, $native, $number_kind); impl<O: ByteOrder> Debug for $name<O> { #[inline] @@ -495,22 +599,61 @@ module!(little_endian, LittleEndian, "little-endian"); module!(network_endian, NetworkEndian, "network-endian"); module!(native_endian, NativeEndian, "native-endian"); -#[cfg(test)] +#[cfg(any(test, kani))] mod tests { use ::byteorder::NativeEndian; - use rand::{ - distributions::{Distribution, Standard}, - rngs::SmallRng, - Rng, SeedableRng, - }; use { super::*, crate::{AsBytes, FromBytes, Unaligned}, }; + #[cfg(not(kani))] + mod compatibility { + pub(super) use rand::{ + distributions::{Distribution, Standard}, + rngs::SmallRng, + Rng, SeedableRng, + }; + + pub(crate) trait Arbitrary {} + + impl<T> Arbitrary for T {} + } + + #[cfg(kani)] + mod compatibility { + pub(crate) use kani::Arbitrary; + + pub(crate) struct SmallRng; + + impl SmallRng { + pub(crate) fn seed_from_u64(_state: u64) -> Self { + Self + } + } + + pub(crate) trait Rng { + fn sample<T, D: Distribution<T>>(&mut self, _distr: D) -> T + where + T: Arbitrary, + { + kani::any() + } + } + + impl Rng for SmallRng {} + + pub(crate) trait Distribution<T> {} + impl<T, U> Distribution<T> for U {} + + pub(crate) struct Standard; + } + + use compatibility::*; + // A native integer type (u16, i32, etc). - trait Native: FromBytes + AsBytes + Copy + PartialEq + Debug { + trait Native: Arbitrary + FromBytes + AsBytes + Copy + PartialEq + Debug { const ZERO: Self; const MAX_VALUE: Self; @@ -520,6 +663,30 @@ mod tests { fn rand<R: Rng>(rng: &mut R) -> Self { rng.sample(Self::DIST) } + + #[cfg(kani)] + fn any() -> Self { + kani::any() + } + + fn checked_add(self, rhs: Self) -> Option<Self>; + fn checked_div(self, rhs: Self) -> Option<Self>; + fn checked_mul(self, rhs: Self) -> Option<Self>; + fn checked_rem(self, rhs: Self) -> Option<Self>; + fn checked_sub(self, rhs: Self) -> Option<Self>; + fn checked_shl(self, rhs: Self) -> Option<Self>; + fn checked_shr(self, rhs: Self) -> Option<Self>; + + fn is_nan(self) -> bool; + + /// For `f32` and `f64`, NaN values are not considered equal to + /// themselves. This method is like `assert_eq!`, but it treats NaN + /// values as equal. + fn assert_eq_or_nan(self, other: Self) { + let slf = (!self.is_nan()).then(|| self); + let other = (!other.is_nan()).then(|| other); + assert_eq!(slf, other); + } } trait ByteArray: @@ -540,6 +707,15 @@ mod tests { fn set(&mut self, native: Self::Native); fn from_bytes(bytes: Self::ByteArray) -> Self; fn into_bytes(self) -> Self::ByteArray; + + /// For `f32` and `f64`, NaN values are not considered equal to + /// themselves. This method is like `assert_eq!`, but it treats NaN + /// values as equal. + fn assert_eq_or_nan(self, other: Self) { + let slf = (!self.get().is_nan()).then(|| self); + let other = (!other.get().is_nan()).then(|| other); + assert_eq!(slf, other); + } } trait ByteOrderTypeUnsigned: ByteOrderType { @@ -572,7 +748,7 @@ mod tests { } macro_rules! impl_traits { - ($name:ident, $native:ident, $bytes:expr, $sign:ident) => { + ($name:ident, $native:ident, $bytes:expr, $sign:ident $(, @$float:ident)?) => { impl Native for $native { // For some types, `0 as $native` is required (for example, when // `$native` is a floating-point type; `0` is an integer), but @@ -584,6 +760,8 @@ mod tests { type Distribution = Standard; const DIST: Standard = Standard; + + impl_traits!(@float_dependent_methods $(@$float)?); } impl<O: ByteOrder> ByteOrderType for $name<O> { @@ -615,6 +793,26 @@ mod tests { impl_byte_order_type_unsigned!($name, $sign); }; + (@float_dependent_methods) => { + fn checked_add(self, rhs: Self) -> Option<Self> { self.checked_add(rhs) } + fn checked_div(self, rhs: Self) -> Option<Self> { self.checked_div(rhs) } + fn checked_mul(self, rhs: Self) -> Option<Self> { self.checked_mul(rhs) } + fn checked_rem(self, rhs: Self) -> Option<Self> { self.checked_rem(rhs) } + fn checked_sub(self, rhs: Self) -> Option<Self> { self.checked_sub(rhs) } + fn checked_shl(self, rhs: Self) -> Option<Self> { self.checked_shl(rhs.try_into().unwrap_or(u32::MAX)) } + fn checked_shr(self, rhs: Self) -> Option<Self> { self.checked_shr(rhs.try_into().unwrap_or(u32::MAX)) } + fn is_nan(self) -> bool { false } + }; + (@float_dependent_methods @float) => { + fn checked_add(self, rhs: Self) -> Option<Self> { Some(self + rhs) } + fn checked_div(self, rhs: Self) -> Option<Self> { Some(self / rhs) } + fn checked_mul(self, rhs: Self) -> Option<Self> { Some(self * rhs) } + fn checked_rem(self, rhs: Self) -> Option<Self> { Some(self % rhs) } + fn checked_sub(self, rhs: Self) -> Option<Self> { Some(self - rhs) } + fn checked_shl(self, _rhs: Self) -> Option<Self> { unimplemented!() } + fn checked_shr(self, _rhs: Self) -> Option<Self> { unimplemented!() } + fn is_nan(self) -> bool { self.is_nan() } + }; } impl_traits!(U16, u16, 2, unsigned); @@ -625,30 +823,39 @@ mod tests { impl_traits!(I32, i32, 4, signed); impl_traits!(I64, i64, 8, signed); impl_traits!(I128, i128, 16, signed); - impl_traits!(F32, f32, 4, signed); - impl_traits!(F64, f64, 8, signed); + impl_traits!(F32, f32, 4, signed, @float); + impl_traits!(F64, f64, 8, signed, @float); - macro_rules! call_for_all_types { + macro_rules! call_for_unsigned_types { ($fn:ident, $byteorder:ident) => { $fn::<U16<$byteorder>>(); $fn::<U32<$byteorder>>(); $fn::<U64<$byteorder>>(); $fn::<U128<$byteorder>>(); + }; + } + + macro_rules! call_for_signed_types { + ($fn:ident, $byteorder:ident) => { $fn::<I16<$byteorder>>(); $fn::<I32<$byteorder>>(); $fn::<I64<$byteorder>>(); $fn::<I128<$byteorder>>(); + }; + } + + macro_rules! call_for_float_types { + ($fn:ident, $byteorder:ident) => { $fn::<F32<$byteorder>>(); $fn::<F64<$byteorder>>(); }; } - macro_rules! call_for_unsigned_types { + macro_rules! call_for_all_types { ($fn:ident, $byteorder:ident) => { - $fn::<U16<$byteorder>>(); - $fn::<U32<$byteorder>>(); - $fn::<U64<$byteorder>>(); - $fn::<U128<$byteorder>>(); + call_for_unsigned_types!($fn, $byteorder); + call_for_signed_types!($fn, $byteorder); + call_for_float_types!($fn, $byteorder); }; } @@ -663,7 +870,7 @@ mod tests { // conditional compilation by `target_pointer_width`. const RNG_SEED: u64 = 0x7A03CAE2F32B5B8F; - const RAND_ITERS: usize = if cfg!(miri) { + const RAND_ITERS: usize = if cfg!(any(miri, kani)) { // The tests below which use this constant used to take a very long time // on Miri, which slows down local development and CI jobs. We're not // using Miri to check for the correctness of our code, but rather its @@ -687,7 +894,8 @@ mod tests { 1024 }; - #[test] + #[cfg_attr(test, test)] + #[cfg_attr(kani, kani::proof)] fn test_zero() { fn test_zero<T: ByteOrderType>() { assert_eq!(T::ZERO.get(), T::Native::ZERO); @@ -697,7 +905,8 @@ mod tests { call_for_all_types!(test_zero, NonNativeEndian); } - #[test] + #[cfg_attr(test, test)] + #[cfg_attr(kani, kani::proof)] fn test_max_value() { fn test_max_value<T: ByteOrderTypeUnsigned>() { assert_eq!(T::MAX_VALUE.get(), T::Native::MAX_VALUE); @@ -707,55 +916,152 @@ mod tests { call_for_unsigned_types!(test_max_value, NonNativeEndian); } - #[test] - fn test_native_endian() { - fn test_native_endian<T: ByteOrderType>() { + #[cfg_attr(test, test)] + #[cfg_attr(kani, kani::proof)] + fn test_endian() { + fn test<T: ByteOrderType>(invert: bool) { let mut r = SmallRng::seed_from_u64(RNG_SEED); for _ in 0..RAND_ITERS { let native = T::Native::rand(&mut r); let mut bytes = T::ByteArray::default(); bytes.as_bytes_mut().copy_from_slice(native.as_bytes()); + if invert { + bytes = bytes.invert(); + } let mut from_native = T::new(native); let from_bytes = T::from_bytes(bytes); - assert_eq!(from_native, from_bytes); - assert_eq!(from_native.get(), native); - assert_eq!(from_bytes.get(), native); + + from_native.assert_eq_or_nan(from_bytes); + from_native.get().assert_eq_or_nan(native); + from_bytes.get().assert_eq_or_nan(native); + assert_eq!(from_native.into_bytes(), bytes); assert_eq!(from_bytes.into_bytes(), bytes); let updated = T::Native::rand(&mut r); from_native.set(updated); - assert_eq!(from_native.get(), updated); + from_native.get().assert_eq_or_nan(updated); } } - call_for_all_types!(test_native_endian, NativeEndian); + fn test_native<T: ByteOrderType>() { + test::<T>(false); + } + + fn test_non_native<T: ByteOrderType>() { + test::<T>(true); + } + + call_for_all_types!(test_native, NativeEndian); + call_for_all_types!(test_non_native, NonNativeEndian); } #[test] - fn test_non_native_endian() { - fn test_non_native_endian<T: ByteOrderType>() { + fn test_ops_impls() { + // Test implementations of traits in `core::ops`. Some of these are + // fairly banal, but some are optimized to perform the operation without + // swapping byte order (namely, bit-wise operations which are identical + // regardless of byte order). These are important to test, and while + // we're testing those anyway, it's trivial to test all of the impls. + + fn test<T, F, G, H>(op: F, op_native: G, op_native_checked: Option<H>) + where + T: ByteOrderType, + F: Fn(T, T) -> T, + G: Fn(T::Native, T::Native) -> T::Native, + H: Fn(T::Native, T::Native) -> Option<T::Native>, + { let mut r = SmallRng::seed_from_u64(RNG_SEED); for _ in 0..RAND_ITERS { - let native = T::Native::rand(&mut r); - let mut bytes = T::ByteArray::default(); - bytes.as_bytes_mut().copy_from_slice(native.as_bytes()); - bytes = bytes.invert(); - let mut from_native = T::new(native); - let from_bytes = T::from_bytes(bytes); - assert_eq!(from_native, from_bytes); - assert_eq!(from_native.get(), native); - assert_eq!(from_bytes.get(), native); - assert_eq!(from_native.into_bytes(), bytes); - assert_eq!(from_bytes.into_bytes(), bytes); + let n0 = T::Native::rand(&mut r); + let n1 = T::Native::rand(&mut r); + let t0 = T::new(n0); + let t1 = T::new(n1); + + // If this operation would overflow/underflow, skip it rather + // than attempt to catch and recover from panics. + if matches!(&op_native_checked, Some(checked) if checked(n0, n1).is_none()) { + continue; + } - let updated = T::Native::rand(&mut r); - from_native.set(updated); - assert_eq!(from_native.get(), updated); + let n_res = op_native(n0, n1); + let t_res = op(t0, t1); + + // For `f32` and `f64`, NaN values are not considered equal to + // themselves. We store `Option<f32>`/`Option<f64>` and store + // NaN as `None` so they can still be compared. + let n_res = (!T::Native::is_nan(n_res)).then(|| n_res); + let t_res = (!T::Native::is_nan(t_res.get())).then(|| t_res.get()); + assert_eq!(n_res, t_res); } } - call_for_all_types!(test_non_native_endian, NonNativeEndian); + macro_rules! test { + (@binary $trait:ident, $method:ident $([$checked_method:ident])?, $($call_for_macros:ident),*) => {{ + test!( + @inner $trait, + core::ops::$trait::$method, + core::ops::$trait::$method, + { + #[allow(unused_mut, unused_assignments)] + let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>; + $( + op_native_checked = Some(T::Native::$checked_method); + )? + op_native_checked + }, + $($call_for_macros),* + ); + }}; + (@unary $trait:ident, $method:ident $([$checked_method:ident])?, $($call_for_macros:ident),*) => {{ + test!( + @inner $trait, + |slf, _rhs| core::ops::$trait::$method(slf), + |slf, _rhs| core::ops::$trait::$method(slf), + { + #[allow(unused_mut, unused_assignments)] + let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>; + $( + op_native_checked = Some(|slf, _rhs| T::Native::$checked_method(slf)); + )? + op_native_checked + }, + $($call_for_macros),* + ); + }}; + (@inner $trait:ident, $op:expr, $op_native:expr, $op_native_checked:expr, $($call_for_macros:ident),*) => {{ + fn t<T: ByteOrderType + core::ops::$trait<Output = T>>() + where + T::Native: core::ops::$trait<Output = T::Native>, + { + test::<T, _, _, _>( + $op, + $op_native, + $op_native_checked, + ); + } + + $( + $call_for_macros!(t, NativeEndian); + $call_for_macros!(t, NonNativeEndian); + )* + }}; + } + + test!(@binary Add, add[checked_add], call_for_all_types); + test!(@binary Div, div[checked_div], call_for_all_types); + test!(@binary Mul, mul[checked_mul], call_for_all_types); + test!(@binary Rem, rem[checked_rem], call_for_all_types); + test!(@binary Sub, sub[checked_sub], call_for_all_types); + + test!(@binary BitAnd, bitand, call_for_unsigned_types, call_for_signed_types); + test!(@binary BitOr, bitor, call_for_unsigned_types, call_for_signed_types); + test!(@binary BitXor, bitxor, call_for_unsigned_types, call_for_signed_types); + test!(@binary Shl, shl[checked_shl], call_for_unsigned_types, call_for_signed_types); + test!(@binary Shr, shr[checked_shr], call_for_unsigned_types, call_for_signed_types); + + test!(@unary Not, not, call_for_signed_types, call_for_unsigned_types); + test!(@unary Neg, neg, call_for_signed_types, call_for_float_types); } #[test] diff --git a/src/derive_util.rs b/src/derive_util.rs deleted file mode 100644 index edf88e3..0000000 --- a/src/derive_util.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2022 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! Utilities used by `zerocopy-derive`. -//! -//! These are defined in `zerocopy` rather than in code generated by -//! `zerocopy-derive` so that they can be compiled once rather than recompiled -//! for every pair of type and trait (in other words, if they were defined in -//! generated code, then deriving `AsBytes` and `FromBytes` on three different -//! types would result in the code in question being emitted and compiled six -//! different times). - -#![allow(missing_debug_implementations)] - -use core::marker::PhantomData; - -/// A compile-time check that should be one particular value. -pub trait ShouldBe<const VALUE: bool> {} - -/// A struct for checking whether `T` contains padding. -pub struct HasPadding<T: ?Sized, const VALUE: bool>(PhantomData<T>); - -impl<T: ?Sized, const VALUE: bool> ShouldBe<VALUE> for HasPadding<T, VALUE> {} - -/// Does the struct type `$t` have padding? -/// -/// `$ts` is the list of the type of every field in `$t`. `$t` must be a -/// struct type, or else `struct_has_padding!`'s result may be meaningless. -/// -/// Note that `struct_has_padding!`'s results are independent of `repr` since -/// they only consider the size of the type and the sizes of the fields. -/// Whatever the repr, the size of the type already takes into account any -/// padding that the compiler has decided to add. Structs with well-defined -/// representations (such as `repr(C)`) can use this macro to check for padding. -/// Note that while this may yield some consistent value for some `repr(Rust)` -/// structs, it is not guaranteed across platforms or compilations. -#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. -#[macro_export] -macro_rules! struct_has_padding { - ($t:ty, $($ts:ty),*) => { - core::mem::size_of::<$t>() > 0 $(+ core::mem::size_of::<$ts>())* - }; -} - -/// Does the union type `$t` have padding? -/// -/// `$ts` is the list of the type of every field in `$t`. `$t` must be a -/// union type, or else `union_has_padding!`'s result may be meaningless. -/// -/// Note that `union_has_padding!`'s results are independent of `repr` since -/// they only consider the size of the type and the sizes of the fields. -/// Whatever the repr, the size of the type already takes into account any -/// padding that the compiler has decided to add. Unions with well-defined -/// representations (such as `repr(C)`) can use this macro to check for padding. -/// Note that while this may yield some consistent value for some `repr(Rust)` -/// unions, it is not guaranteed across platforms or compilations. -#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. -#[macro_export] -macro_rules! union_has_padding { - ($t:ty, $($ts:ty),*) => { - false $(|| core::mem::size_of::<$t>() != core::mem::size_of::<$ts>())* - }; -} - -#[cfg(test)] -mod tests { - use crate::util::testutil::*; - - #[test] - fn test_struct_has_padding() { - // Test that, for each provided repr, `struct_has_padding!` reports the - // expected value. - macro_rules! test { - (#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{ - #[$cfg] - struct Test($($ts),*); - assert_eq!(struct_has_padding!(Test, $($ts),*), $expect); - }}; - (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => { - test!(#[$cfg] ($($ts),*) => $expect); - test!($(#[$cfgs])* ($($ts),*) => $expect); - }; - } - - test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] () => false); - test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8) => false); - test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8, ()) => false); - test!(#[repr(C)] #[repr(packed)] (u8, u8) => false); - - test!(#[repr(C)] (u8, AU64) => true); - // Rust won't let you put `#[repr(packed)]` on a type which contains a - // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. - // It's not ideal, but it definitely has align > 1 on /some/ of our CI - // targets, and this isn't a particularly complex macro we're testing - // anyway. - test!(#[repr(packed)] (u8, u64) => false); - } - - #[test] - fn test_union_has_padding() { - // Test that, for each provided repr, `union_has_padding!` reports the - // expected value. - macro_rules! test { - (#[$cfg:meta] {$($fs:ident: $ts:ty),*} => $expect:expr) => {{ - #[$cfg] - #[allow(unused)] // fields are never read - union Test{ $($fs: $ts),* } - assert_eq!(union_has_padding!(Test, $($ts),*), $expect); - }}; - (#[$cfg:meta] $(#[$cfgs:meta])* {$($fs:ident: $ts:ty),*} => $expect:expr) => { - test!(#[$cfg] {$($fs: $ts),*} => $expect); - test!($(#[$cfgs])* {$($fs: $ts),*} => $expect); - }; - } - - test!(#[repr(C)] #[repr(packed)] {a: u8} => false); - test!(#[repr(C)] #[repr(packed)] {a: u8, b: u8} => false); - - // Rust won't let you put `#[repr(packed)]` on a type which contains a - // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. - // It's not ideal, but it definitely has align > 1 on /some/ of our CI - // targets, and this isn't a particularly complex macro we're testing - // anyway. - test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => true); - } -} @@ -1,6 +1,11 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2018 The Fuchsia Authors +// +// Licensed under the 2-Clause BSD License <LICENSE-BSD or +// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. // After updating the following doc comment, make sure to run the following // command to update `README.md` based on its contents: @@ -32,7 +37,7 @@ //! byte sequences with little to no runtime overhead. //! //! Zerocopy also provides byte-order aware integer types that support these -//! conversions; see the `byteorder` module. These types are especially useful +//! conversions; see the [`byteorder`] module. These types are especially useful //! for network parsing. //! //! [user-survey]: https://docs.google.com/forms/d/e/1FAIpQLSdzBNTN9tzwsmtyZxRFNL02K36IWCdHWW2ZBckyQS2xiO3i8Q/viewform?usp=published_options @@ -84,6 +89,59 @@ //! may be removed at any point in the future. //! //! [simd-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html +//! +//! # Security Ethos +//! +//! Zerocopy is expressly designed for use in security-critical contexts. We +//! strive to ensure that that zerocopy code is sound under Rust's current +//! memory model, and *any future memory model*. We ensure this by: +//! - **...not 'guessing' about Rust's semantics.** +//! We annotate `unsafe` code with a precise rationale for its soundness that +//! cites a relevant section of Rust's official documentation. When Rust's +//! documented semantics are unclear, we work with the Rust Operational +//! Semantics Team to clarify Rust's documentation. +//! - **...rigorously testing our implementation.** +//! We run tests using [Miri], ensuring that zerocopy is sound across a wide +//! array of supported target platforms of varying endianness and pointer +//! width, and across both current and experimental memory models of Rust. +//! - **...formally proving the correctness of our implementation.** +//! We apply formal verification tools like [Kani][kani] to prove zerocopy's +//! correctness. +//! +//! For more information, see our full [soundness policy]. +//! +//! [Miri]: https://github.com/rust-lang/miri +//! [Kani]: https://github.com/model-checking/kani +//! [soundness policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#soundness +//! +//! # Relationship to Project Safe Transmute +//! +//! [Project Safe Transmute] is an official initiative of the Rust Project to +//! develop language-level support for safer transmutation. The Project consults +//! with crates like zerocopy to identify aspects of safer transmutation that +//! would benefit from compiler support, and has developed an [experimental, +//! compiler-supported analysis][mcp-transmutability] which determines whether, +//! for a given type, any value of that type may be soundly transmuted into +//! another type. Once this functionality is sufficiently mature, zerocopy +//! intends to replace its internal transmutability analysis (implemented by our +//! custom derives) with the compiler-supported one. This change will likely be +//! an implementation detail that is invisible to zerocopy's users. +//! +//! Project Safe Transmute will not replace the need for most of zerocopy's +//! higher-level abstractions. The experimental compiler analysis is a tool for +//! checking the soundness of `unsafe` code, not a tool to avoid writing +//! `unsafe` code altogether. For the foreseeable future, crates like zerocopy +//! will still be required in order to provide higher-level abstractions on top +//! of the building block provided by Project Safe Transmute. +//! +//! [Project Safe Transmute]: https://rust-lang.github.io/rfcs/2835-project-safe-transmute.html +//! [mcp-transmutability]: https://github.com/rust-lang/compiler-team/issues/411 +//! +//! # MSRV +//! +//! See our [MSRV policy]. +//! +//! [MSRV policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#msrv // Sometimes we want to use lints which were added after our MSRV. // `unknown_lints` is `warn` by default and we deny warnings in CI, so without @@ -109,6 +167,10 @@ unused_qualifications, variant_size_differences )] +#![cfg_attr( + __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, + deny(fuzzy_provenance_casts, lossy_provenance_casts) +)] #![deny( clippy::all, clippy::alloc_instead_of_core, @@ -147,7 +209,7 @@ )] // In test code, it makes sense to weight more heavily towards concise, readable // code over correct or debuggable code. -#![cfg_attr(test, allow( +#![cfg_attr(any(test, kani), allow( // In tests, you get line numbers and have access to source code, so panic // messages are less important. You also often unwrap a lot, which would // make expect'ing instead very verbose. @@ -161,27 +223,48 @@ ))] #![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "simd-nightly", feature(stdsimd))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![cfg_attr( + __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, + feature(layout_for_ptr, strict_provenance) +)] + +// This is a hack to allow zerocopy-derive derives to work in this crate. They +// assume that zerocopy is linked as an extern crate, so they access items from +// it as `zerocopy::Xxx`. This makes that still work. +#[cfg(any(feature = "derive", test))] +extern crate self as zerocopy; #[macro_use] mod macros; #[cfg(feature = "byteorder")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "byteorder")))] pub mod byteorder; -#[cfg(any(feature = "derive", test))] #[doc(hidden)] -pub mod derive_util; -// TODO(#252): If we make this pub, come up with a better name. +pub mod macro_util; mod util; +// TODO(#252): If we make this pub, come up with a better name. mod wrappers; #[cfg(feature = "byteorder")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "byteorder")))] pub use crate::byteorder::*; pub use crate::wrappers::*; + #[cfg(any(feature = "derive", test))] -pub use zerocopy_derive::*; +#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] +pub use zerocopy_derive::Unaligned; + +// `pub use` separately here so that we can mark it `#[doc(hidden)]`. +// +// TODO(#29): Remove this or add a doc comment. +#[cfg(any(feature = "derive", test))] +#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] +#[doc(hidden)] +pub use zerocopy_derive::KnownLayout; use core::{ - alloc::Layout, cell::{self, RefMut}, cmp::Ordering, fmt::{self, Debug, Display, Formatter}, @@ -193,63 +276,202 @@ use core::{ NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping, }, ops::{Deref, DerefMut}, - ptr, slice, + ptr::{self, NonNull}, + slice, }; #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "alloc")] -use { - alloc::{boxed::Box, vec::Vec}, - core::ptr::NonNull, +use alloc::{boxed::Box, vec::Vec}; + +#[cfg(any(feature = "alloc", kani))] +use core::alloc::Layout; + +// Used by `TryFromBytes::is_bit_valid`. +#[doc(hidden)] +pub use crate::util::ptr::Ptr; + +// For each polyfill, as soon as the corresponding feature is stable, the +// polyfill import will be unused because method/function resolution will prefer +// the inherent method/function over a trait method/function. Thus, we suppress +// the `unused_imports` warning. +// +// See the documentation on `util::polyfills` for more information. +#[allow(unused_imports)] +use crate::util::polyfills::NonNullExt as _; + +#[rustversion::nightly] +#[cfg(all(test, not(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)))] +const _: () = { + #[deprecated = "some tests may be skipped due to missing RUSTFLAGS=\"--cfg __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS\""] + const _WARNING: () = (); + #[warn(deprecated)] + _WARNING }; -// This is a hack to allow zerocopy-derive derives to work in this crate. They -// assume that zerocopy is linked as an extern crate, so they access items from -// it as `zerocopy::Xxx`. This makes that still work. -#[cfg(any(feature = "derive", test))] -mod zerocopy { - pub(crate) use crate::*; -} +/// The target pointer width, counted in bits. +const POINTER_WIDTH_BITS: usize = mem::size_of::<usize>() * 8; /// The layout of a type which might be dynamically-sized. /// -/// `DstLayout` describes the layout of sized types, slice types, and "custom +/// `DstLayout` describes the layout of sized types, slice types, and "slice /// DSTs" - ie, those that are known by the type system to have a trailing slice /// (as distinguished from `dyn Trait` types - such types *might* have a /// trailing slice type, but the type system isn't aware of it). +/// +/// # Safety +/// +/// Unlike [`core::alloc::Layout`], `DstLayout` is only used to describe full +/// Rust types - ie, those that satisfy the layout requirements outlined by +/// [the reference]. Callers may assume that an instance of `DstLayout` +/// satisfies any conditions imposed on Rust types by the reference. +/// +/// If `layout: DstLayout` describes a type, `T`, then it is guaranteed that: +/// - `layout.align` is equal to `T`'s alignment +/// - If `layout.size_info` is `SizeInfo::Sized { size }`, then `T: Sized` and +/// `size_of::<T>() == size` +/// - If `layout.size_info` is `SizeInfo::SliceDst(slice_layout)`, then +/// - `T` is a slice DST +/// - The `size` of an instance of `T` with `elems` trailing slice elements is +/// equal to `slice_layout.offset + slice_layout.elem_size * elems` rounded up +/// to the nearest multiple of `layout.align`. Any bytes in the range +/// `[slice_layout.offset + slice_layout.elem_size * elems, size)` are padding +/// and must not be assumed to be initialized. +/// +/// [the reference]: https://doc.rust-lang.org/reference/type-layout.html #[doc(hidden)] #[allow(missing_debug_implementations, missing_copy_implementations)] -#[cfg_attr(test, derive(Copy, Clone, Debug, PartialEq, Eq))] +#[cfg_attr(any(kani, test), derive(Copy, Clone, Debug, PartialEq, Eq))] pub struct DstLayout { - /// The base size and the alignment of the type: - /// - For sized types, the size encoded by this `Layout` is - /// `size_of::<T>()`. For DSTs, the size represents the size of the type - /// when the trailing slice field contains 0 elements. - /// - For all types, the alignment represents the alignment of the type. - // TODO: If we end up replacing this with separate size and alignment to - // make Kani happy, file an issue to eventually adopt the stdlib's - // `Alignment` type trick. - _base_layout: Layout, - /// For sized types, `None`. For DSTs, the size of the element type of the - /// trailing slice. - _trailing_slice_elem_size: Option<usize>, -} - -#[cfg_attr(test, derive(Copy, Clone, Debug))] -enum _CastType { + align: NonZeroUsize, + size_info: SizeInfo, +} + +#[cfg_attr(any(kani, test), derive(Copy, Clone, Debug, PartialEq, Eq))] +enum SizeInfo<E = usize> { + Sized { _size: usize }, + SliceDst(TrailingSliceLayout<E>), +} + +#[cfg_attr(any(kani, test), derive(Copy, Clone, Debug, PartialEq, Eq))] +struct TrailingSliceLayout<E = usize> { + // The offset of the first byte of the trailing slice field. Note that this + // is NOT the same as the minimum size of the type. For example, consider + // the following type: + // + // struct Foo { + // a: u16, + // b: u8, + // c: [u8], + // } + // + // In `Foo`, `c` is at byte offset 3. When `c.len() == 0`, `c` is followed + // by a padding byte. + _offset: usize, + // The size of the element type of the trailing slice field. + _elem_size: E, +} + +impl SizeInfo { + /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a + /// `NonZeroUsize`. If `elem_size` is 0, returns `None`. + #[allow(unused)] + const fn try_to_nonzero_elem_size(&self) -> Option<SizeInfo<NonZeroUsize>> { + Some(match *self { + SizeInfo::Sized { _size } => SizeInfo::Sized { _size }, + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) => { + if let Some(_elem_size) = NonZeroUsize::new(_elem_size) { + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) + } else { + return None; + } + } + }) + } +} + +#[doc(hidden)] +#[derive(Copy, Clone)] +#[cfg_attr(test, derive(Debug))] +#[allow(missing_debug_implementations)] +pub enum _CastType { _Prefix, _Suffix, } impl DstLayout { + /// The minimum possible alignment of a type. + const MIN_ALIGN: NonZeroUsize = match NonZeroUsize::new(1) { + Some(min_align) => min_align, + None => unreachable!(), + }; + + /// The maximum theoretic possible alignment of a type. + /// + /// For compatibility with future Rust versions, this is defined as the + /// maximum power-of-two that fits into a `usize`. See also + /// [`DstLayout::CURRENT_MAX_ALIGN`]. + const THEORETICAL_MAX_ALIGN: NonZeroUsize = + match NonZeroUsize::new(1 << (POINTER_WIDTH_BITS - 1)) { + Some(max_align) => max_align, + None => unreachable!(), + }; + + /// The current, documented max alignment of a type \[1\]. + /// + /// \[1\] Per <https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers>: + /// + /// The alignment value must be a power of two from 1 up to + /// 2<sup>29</sup>. + #[cfg(not(kani))] + const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 28) { + Some(max_align) => max_align, + None => unreachable!(), + }; + + /// Constructs a `DstLayout` for a zero-sized type with `repr_align` + /// alignment (or 1). If `repr_align` is provided, then it must be a power + /// of two. + /// + /// # Panics + /// + /// This function panics if the supplied `repr_align` is not a power of two. + /// + /// # Safety + /// + /// Unsafe code may assume that the contract of this function is satisfied. + #[doc(hidden)] + #[inline] + pub const fn new_zst(repr_align: Option<NonZeroUsize>) -> DstLayout { + let align = match repr_align { + Some(align) => align, + None => Self::MIN_ALIGN, + }; + + assert!(align.is_power_of_two()); + + DstLayout { align, size_info: SizeInfo::Sized { _size: 0 } } + } + /// Constructs a `DstLayout` which describes `T`. /// /// # Safety /// /// Unsafe code may assume that `DstLayout` is the correct layout for `T`. - const fn for_type<T>() -> DstLayout { - DstLayout { _base_layout: Layout::new::<T>(), _trailing_slice_elem_size: None } + #[doc(hidden)] + #[inline] + pub const fn for_type<T>() -> DstLayout { + // SAFETY: `align` is correct by construction. `T: Sized`, and so it is + // sound to initialize `size_info` to `SizeInfo::Sized { size }`; the + // `size` field is also correct by construction. + DstLayout { + align: match NonZeroUsize::new(mem::align_of::<T>()) { + Some(align) => align, + None => unreachable!(), + }, + size_info: SizeInfo::Sized { _size: mem::size_of::<T>() }, + } } /// Constructs a `DstLayout` which describes `[T]`. @@ -258,17 +480,216 @@ impl DstLayout { /// /// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`. const fn for_slice<T>() -> DstLayout { + // SAFETY: The alignment of a slice is equal to the alignment of its + // element type, and so `align` is initialized correctly. + // + // Since this is just a slice type, there is no offset between the + // beginning of the type and the beginning of the slice, so it is + // correct to set `offset: 0`. The `elem_size` is correct by + // construction. Since `[T]` is a (degenerate case of a) slice DST, it + // is correct to initialize `size_info` to `SizeInfo::SliceDst`. DstLayout { - // SAFETY: `[T; 0]` has the same alignment as `T`, but zero size. - // [1] A slice of length 0 has no size, so 0 is the correct size for - // the base of the type. - // - // [1] https://doc.rust-lang.org/reference/type-layout.html#array-layout - _base_layout: Layout::new::<[T; 0]>(), - _trailing_slice_elem_size: Some(mem::size_of::<T>()), + align: match NonZeroUsize::new(mem::align_of::<T>()) { + Some(align) => align, + None => unreachable!(), + }, + size_info: SizeInfo::SliceDst(TrailingSliceLayout { + _offset: 0, + _elem_size: mem::size_of::<T>(), + }), } } + /// Like `Layout::extend`, this creates a layout that describes a record + /// whose layout consists of `self` followed by `next` that includes the + /// necessary inter-field padding, but not any trailing padding. + /// + /// In order to match the layout of a `#[repr(C)]` struct, this method + /// should be invoked for each field in declaration order. To add trailing + /// padding, call `DstLayout::pad_to_align` after extending the layout for + /// all fields. If `self` corresponds to a type marked with + /// `repr(packed(N))`, then `repr_packed` should be set to `Some(N)`, + /// otherwise `None`. + /// + /// This method cannot be used to match the layout of a record with the + /// default representation, as that representation is mostly unspecified. + /// + /// # Safety + /// + /// If a (potentially hypothetical) valid `repr(C)` Rust type begins with + /// fields whose layout are `self`, and those fields are immediately + /// followed by a field whose layout is `field`, then unsafe code may rely + /// on `self.extend(field, repr_packed)` producing a layout that correctly + /// encompasses those two components. + /// + /// We make no guarantees to the behavior of this method if these fragments + /// cannot appear in a valid Rust type (e.g., the concatenation of the + /// layouts would lead to a size larger than `isize::MAX`). + #[doc(hidden)] + #[inline] + pub const fn extend(self, field: DstLayout, repr_packed: Option<NonZeroUsize>) -> Self { + use util::{core_layout::padding_needed_for, max, min}; + + // If `repr_packed` is `None`, there are no alignment constraints, and + // the value can be defaulted to `THEORETICAL_MAX_ALIGN`. + let max_align = match repr_packed { + Some(max_align) => max_align, + None => Self::THEORETICAL_MAX_ALIGN, + }; + + assert!(max_align.is_power_of_two()); + + // We use Kani to prove that this method is robust to future increases + // in Rust's maximum allowed alignment. However, if such a change ever + // actually occurs, we'd like to be notified via assertion failures. + #[cfg(not(kani))] + { + debug_assert!(self.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); + debug_assert!(field.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); + if let Some(repr_packed) = repr_packed { + debug_assert!(repr_packed.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); + } + } + + // The field's alignment is clamped by `repr_packed` (i.e., the + // `repr(packed(N))` attribute, if any) [1]. + // + // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: + // + // The alignments of each field, for the purpose of positioning + // fields, is the smaller of the specified alignment and the alignment + // of the field's type. + let field_align = min(field.align, max_align); + + // The struct's alignment is the maximum of its previous alignment and + // `field_align`. + let align = max(self.align, field_align); + + let size_info = match self.size_info { + // If the layout is already a DST, we panic; DSTs cannot be extended + // with additional fields. + SizeInfo::SliceDst(..) => panic!("Cannot extend a DST with additional fields."), + + SizeInfo::Sized { _size: preceding_size } => { + // Compute the minimum amount of inter-field padding needed to + // satisfy the field's alignment, and offset of the trailing + // field. [1] + // + // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: + // + // Inter-field padding is guaranteed to be the minimum + // required in order to satisfy each field's (possibly + // altered) alignment. + let padding = padding_needed_for(preceding_size, field_align); + + // This will not panic (and is proven to not panic, with Kani) + // if the layout components can correspond to a leading layout + // fragment of a valid Rust type, but may panic otherwise (e.g., + // combining or aligning the components would create a size + // exceeding `isize::MAX`). + let offset = match preceding_size.checked_add(padding) { + Some(offset) => offset, + None => panic!("Adding padding to `self`'s size overflows `usize`."), + }; + + match field.size_info { + SizeInfo::Sized { _size: field_size } => { + // If the trailing field is sized, the resulting layout + // will be sized. Its size will be the sum of the + // preceeding layout, the size of the new field, and the + // size of inter-field padding between the two. + // + // This will not panic (and is proven with Kani to not + // panic) if the layout components can correspond to a + // leading layout fragment of a valid Rust type, but may + // panic otherwise (e.g., combining or aligning the + // components would create a size exceeding + // `usize::MAX`). + let size = match offset.checked_add(field_size) { + Some(size) => size, + None => panic!("`field` cannot be appended without the total size overflowing `usize`"), + }; + SizeInfo::Sized { _size: size } + } + SizeInfo::SliceDst(TrailingSliceLayout { + _offset: trailing_offset, + _elem_size, + }) => { + // If the trailing field is dynamically sized, so too + // will the resulting layout. The offset of the trailing + // slice component is the sum of the offset of the + // trailing field and the trailing slice offset within + // that field. + // + // This will not panic (and is proven with Kani to not + // panic) if the layout components can correspond to a + // leading layout fragment of a valid Rust type, but may + // panic otherwise (e.g., combining or aligning the + // components would create a size exceeding + // `usize::MAX`). + let offset = match offset.checked_add(trailing_offset) { + Some(offset) => offset, + None => panic!("`field` cannot be appended without the total size overflowing `usize`"), + }; + SizeInfo::SliceDst(TrailingSliceLayout { _offset: offset, _elem_size }) + } + } + } + }; + + DstLayout { align, size_info } + } + + /// Like `Layout::pad_to_align`, this routine rounds the size of this layout + /// up to the nearest multiple of this type's alignment or `repr_packed` + /// (whichever is less). This method leaves DST layouts unchanged, since the + /// trailing padding of DSTs is computed at runtime. + /// + /// In order to match the layout of a `#[repr(C)]` struct, this method + /// should be invoked after the invocations of [`DstLayout::extend`]. If + /// `self` corresponds to a type marked with `repr(packed(N))`, then + /// `repr_packed` should be set to `Some(N)`, otherwise `None`. + /// + /// This method cannot be used to match the layout of a record with the + /// default representation, as that representation is mostly unspecified. + /// + /// # Safety + /// + /// If a (potentially hypothetical) valid `repr(C)` type begins with fields + /// whose layout are `self` followed only by zero or more bytes of trailing + /// padding (not included in `self`), then unsafe code may rely on + /// `self.pad_to_align(repr_packed)` producing a layout that correctly + /// encapsulates the layout of that type. + /// + /// We make no guarantees to the behavior of this method if `self` cannot + /// appear in a valid Rust type (e.g., because the addition of trailing + /// padding would lead to a size larger than `isize::MAX`). + #[doc(hidden)] + #[inline] + pub const fn pad_to_align(self) -> Self { + use util::core_layout::padding_needed_for; + + let size_info = match self.size_info { + // For sized layouts, we add the minimum amount of trailing padding + // needed to satisfy alignment. + SizeInfo::Sized { _size: unpadded_size } => { + let padding = padding_needed_for(unpadded_size, self.align); + let size = match unpadded_size.checked_add(padding) { + Some(size) => size, + None => panic!("Adding padding caused size to overflow `usize`."), + }; + SizeInfo::Sized { _size: size } + } + // For DST layouts, trailing padding depends on the length of the + // trailing DST and is computed at runtime. This does not alter the + // offset or element size of the layout, so we leave `size_info` + // unchanged. + size_info @ SizeInfo::SliceDst(_) => size_info, + }; + + DstLayout { align: self.align, size_info } + } + /// Validates that a cast is sound from a layout perspective. /// /// Validates that the size and alignment requirements of a type with the @@ -291,10 +712,9 @@ impl DstLayout { /// - A prefix cast is requested, and `addr` does not satisfy `self`'s /// alignment requirement /// - A suffix cast is requested, and `addr + bytes_len` does not satisfy - /// `self`'s alignment requirement (as a consequence, since the size of - /// the trailing slice element is a multiple of the alignment, no length - /// for the trailing slice will result in a starting address which is - /// properly aligned) + /// `self`'s alignment requirement (as a consequence, since all instances + /// of the type are a multiple of its alignment, no size for the type will + /// result in a starting address which is properly aligned) /// /// # Safety /// @@ -325,13 +745,17 @@ impl DstLayout { /// /// # Panics /// + /// `validate_cast_and_convert_metadata` will panic if `self` describes a + /// DST whose trailing slice element is zero-sized. + /// /// If `addr + bytes_len` overflows `usize`, /// `validate_cast_and_convert_metadata` may panic, or it may return /// incorrect results. No guarantees are made about when /// `validate_cast_and_convert_metadata` will panic. The caller should not /// rely on `validate_cast_and_convert_metadata` panicking in any particular /// condition, even if `debug_assertions` are enabled. - const fn _validate_cast_and_convert_metadata( + #[allow(unused)] + const fn validate_cast_and_convert_metadata( &self, addr: usize, bytes_len: usize, @@ -348,133 +772,126 @@ impl DstLayout { }; } - // Note that, in practice, `elem_size` is always a compile-time - // constant. We do this check earlier than needed to ensure that we - // always panic as a result of bugs in the program (such as calling this - // function on an invalid type) instead of allowing this panic to be - // hidden if the cast would have failed anyway for runtime reasons (such - // as a too-small memory region). + // Note that, in practice, `self` is always a compile-time constant. We + // do this check earlier than needed to ensure that we always panic as a + // result of bugs in the program (such as calling this function on an + // invalid type) instead of allowing this panic to be hidden if the cast + // would have failed anyway for runtime reasons (such as a too-small + // memory region). // // TODO(#67): Once our MSRV is 1.65, use let-else: // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements - let elem_size = match self._trailing_slice_elem_size { - Some(elem_size) => match NonZeroUsize::new(elem_size) { - Some(elem_size) => Some(elem_size), - None => panic!("attempted to cast to slice type with zero-sized element"), - }, - None => None, + let size_info = match self.size_info.try_to_nonzero_elem_size() { + Some(size_info) => size_info, + None => panic!("attempted to cast to slice type with zero-sized element"), }; // Precondition __debug_assert!(addr.checked_add(bytes_len).is_some(), "`addr` + `bytes_len` > usize::MAX"); - // We check alignment for `addr` (for prefix casts) or `addr + - // bytes_len` (for suffix casts). For a prefix cast, the correctness of - // this check is trivial - `addr` is the address the object will live - // at. - // - // For a suffix cast, we know that all valid sizes for the type are a - // multiple of the alignment. Thus, a validly-sized instance which lives - // at a validly-aligned address must also end at a validly-aligned - // address. Thus, if the end address for a suffix cast (`addr + - // bytes_len`) is not aligned, then no valid start address will be - // aligned either. - let offset = match cast_type { - _CastType::_Prefix => 0, - _CastType::_Suffix => bytes_len, - }; - - // Addition is guaranteed not to overflow because `offset <= bytes_len`, - // and `addr + bytes_len <= usize::MAX` is a precondition of this - // method. Modulus is guaranteed not to divide by 0 because `.align()` - // guarantees that its return value is non-zero. - #[allow(clippy::arithmetic_side_effects)] - if (addr + offset) % self._base_layout.align() != 0 { - return None; - } - - let base_size = self._base_layout.size(); - - // LEMMA 0: max_slice_bytes + base_size == bytes_len - // - // LEMMA 1: base_size <= bytes_len: - // - If `base_size > bytes_len`, `bytes_len.checked_sub(base_size)` - // returns `None`, and we return. - // - // TODO(#67): Once our MSRV is 1.65, use let-else: - // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements - let max_slice_bytes = if let Some(max_byte_slice) = bytes_len.checked_sub(base_size) { - max_byte_slice - } else { - return None; - }; - - // Lemma 0 - __debug_assert!(max_slice_bytes + base_size == bytes_len); - - // Lemma 1 - __debug_assert!(base_size <= bytes_len); - - let (elems, self_bytes) = if let Some(elem_size) = elem_size { - // Guaranteed not to divide by 0 because `elem_size` is a - // `NonZeroUsize`. - #[allow(clippy::arithmetic_side_effects)] - let elems = max_slice_bytes / elem_size.get(); - - // NOTE: Another option for this step in the algorithm is to set - // `slice_bytes = elems * elem_size`. However, using multiplication - // causes Kani to choke. In practice, the compiler is likely to - // generate identical machine code in both cases. Note that this - // divide-then-mod approach is trivially optimizable into a single - // operation that computes both the quotient and the remainder. - - // First line is guaranteed not to mod by 0 because `elem_size` is a - // `NonZeroUsize`. Second line is guaranteed not to underflow - // because `rem <= max_slice_bytes` thanks to the mod operation. + // Alignment checks go in their own block to avoid introducing variables + // into the top-level scope. + { + // We check alignment for `addr` (for prefix casts) or `addr + + // bytes_len` (for suffix casts). For a prefix cast, the correctness + // of this check is trivial - `addr` is the address the object will + // live at. // - // LEMMA 2: slice_bytes <= max_slice_bytes - #[allow(clippy::arithmetic_side_effects)] - let rem = max_slice_bytes % elem_size.get(); - #[allow(clippy::arithmetic_side_effects)] - let slice_bytes = max_slice_bytes - rem; - - // Lemma 2 - __debug_assert!(slice_bytes <= max_slice_bytes); - - // Guaranteed not to overflow: - // - max_slice_bytes + base_size == bytes_len (lemma 0) - // - slice_bytes <= max_slice_bytes (lemma 2) - // - slice_bytes + base_size <= bytes_len (substitution) ------+ - // - bytes_len <= usize::MAX (bytes_len: usize) | - // - slice_bytes + base_size <= usize::MAX (substitution) | - // | - // LEMMA 3: self_bytes <= bytes_len: | - // - slice_bytes + base_size <= bytes_len <--------------------------+ (reused for lemma) - // - slice_bytes <= bytes_len - #[allow(clippy::arithmetic_side_effects)] - let self_bytes = base_size + slice_bytes; + // For a suffix cast, we know that all valid sizes for the type are + // a multiple of the alignment (and by safety precondition, we know + // `DstLayout` may only describe valid Rust types). Thus, a + // validly-sized instance which lives at a validly-aligned address + // must also end at a validly-aligned address. Thus, if the end + // address for a suffix cast (`addr + bytes_len`) is not aligned, + // then no valid start address will be aligned either. + let offset = match cast_type { + _CastType::_Prefix => 0, + _CastType::_Suffix => bytes_len, + }; - // Lemma 3 - __debug_assert!(self_bytes <= bytes_len); + // Addition is guaranteed not to overflow because `offset <= + // bytes_len`, and `addr + bytes_len <= usize::MAX` is a + // precondition of this method. Modulus is guaranteed not to divide + // by 0 because `align` is non-zero. + #[allow(clippy::arithmetic_side_effects)] + if (addr + offset) % self.align.get() != 0 { + return None; + } + } - (elems, self_bytes) - } else { - (0, base_size) + let (elems, self_bytes) = match size_info { + SizeInfo::Sized { _size: size } => { + if size > bytes_len { + return None; + } + (0, size) + } + SizeInfo::SliceDst(TrailingSliceLayout { _offset: offset, _elem_size: elem_size }) => { + // Calculate the maximum number of bytes that could be consumed + // - any number of bytes larger than this will either not be a + // multiple of the alignment, or will be larger than + // `bytes_len`. + let max_total_bytes = + util::round_down_to_next_multiple_of_alignment(bytes_len, self.align); + // Calculate the maximum number of bytes that could be consumed + // by the trailing slice. + // + // TODO(#67): Once our MSRV is 1.65, use let-else: + // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements + let max_slice_and_padding_bytes = match max_total_bytes.checked_sub(offset) { + Some(max) => max, + // `bytes_len` too small even for 0 trailing slice elements. + None => return None, + }; + + // Calculate the number of elements that fit in + // `max_slice_and_padding_bytes`; any remaining bytes will be + // considered padding. + // + // Guaranteed not to divide by zero: `elem_size` is non-zero. + #[allow(clippy::arithmetic_side_effects)] + let elems = max_slice_and_padding_bytes / elem_size.get(); + // Guaranteed not to overflow on multiplication: `usize::MAX >= + // max_slice_and_padding_bytes >= (max_slice_and_padding_bytes / + // elem_size) * elem_size`. + // + // Guaranteed not to overflow on addition: + // - max_slice_and_padding_bytes == max_total_bytes - offset + // - elems * elem_size <= max_slice_and_padding_bytes == max_total_bytes - offset + // - elems * elem_size + offset <= max_total_bytes <= usize::MAX + #[allow(clippy::arithmetic_side_effects)] + let without_padding = offset + elems * elem_size.get(); + // `self_bytes` is equal to the offset bytes plus the bytes + // consumed by the trailing slice plus any padding bytes + // required to satisfy the alignment. Note that we have computed + // the maximum number of trailing slice elements that could fit + // in `self_bytes`, so any padding is guaranteed to be less than + // the size of an extra element. + // + // Guaranteed not to overflow: + // - By previous comment: without_padding == elems * elem_size + + // offset <= max_total_bytes + // - By construction, `max_total_bytes` is a multiple of + // `self.align`. + // - At most, adding padding needed to round `without_padding` + // up to the next multiple of the alignment will bring + // `self_bytes` up to `max_total_bytes`. + #[allow(clippy::arithmetic_side_effects)] + let self_bytes = without_padding + + util::core_layout::padding_needed_for(without_padding, self.align); + (elems, self_bytes) + } }; - // LEMMA 4: self_bytes <= bytes_len: - // - `if` branch returns `self_bytes`; lemma 3 guarantees `self_bytes <= - // bytes_len` - // - `else` branch returns `base_size`; lemma 1 guarantees `base_size <= - // bytes_len` - - // Lemma 4 __debug_assert!(self_bytes <= bytes_len); let split_at = match cast_type { _CastType::_Prefix => self_bytes, - // Guaranteed not to underflow because `self_bytes <= bytes_len` - // (lemma 4). + // Guaranteed not to underflow: + // - In the `Sized` branch, only returns `size` if `size <= + // bytes_len`. + // - In the `SliceDst` branch, calculates `self_bytes <= + // max_toatl_bytes`, which is upper-bounded by `bytes_len`. #[allow(clippy::arithmetic_side_effects)] _CastType::_Suffix => bytes_len - self_bytes, }; @@ -496,15 +913,44 @@ impl DstLayout { /// /// This trait does not convey any safety guarantees to code outside this crate. #[doc(hidden)] // TODO: Remove this once KnownLayout is used by other APIs -pub unsafe trait KnownLayout: sealed::KnownLayoutSealed { +pub unsafe trait KnownLayout { + // The `Self: Sized` bound makes it so that `KnownLayout` can still be + // object safe. It's not currently object safe thanks to `const LAYOUT`, and + // it likely won't be in the future, but there's no reason not to be + // forwards-compatible with object safety. + #[doc(hidden)] + fn only_derive_is_allowed_to_implement_this_trait() + where + Self: Sized; + #[doc(hidden)] const LAYOUT: DstLayout; + + /// SAFETY: The returned pointer has the same address and provenance as + /// `bytes`. If `Self` is a DST, the returned pointer's referent has `elems` + /// elements in its trailing slice. If `Self` is sized, `elems` is ignored. + #[doc(hidden)] + fn raw_from_ptr_len(bytes: NonNull<u8>, elems: usize) -> NonNull<Self>; } -impl<T: KnownLayout> sealed::KnownLayoutSealed for [T] {} // SAFETY: Delegates safety to `DstLayout::for_slice`. unsafe impl<T: KnownLayout> KnownLayout for [T] { + #[allow(clippy::missing_inline_in_public_items)] + fn only_derive_is_allowed_to_implement_this_trait() + where + Self: Sized, + { + } const LAYOUT: DstLayout = DstLayout::for_slice::<T>(); + + // SAFETY: `.cast` preserves address and provenance. The returned pointer + // refers to an object with `elems` elements by construction. + #[inline(always)] + fn raw_from_ptr_len(data: NonNull<u8>, elems: usize) -> NonNull<Self> { + // TODO(#67): Remove this allow. See NonNullExt for more details. + #[allow(unstable_name_collisions)] + NonNull::slice_from_raw_parts(data.cast::<T>(), elems) + } } #[rustfmt::skip] @@ -521,56 +967,79 @@ impl_known_layout!( T: ?Sized => PhantomData<T>, T => Wrapping<T>, T => MaybeUninit<T>, + T: ?Sized => *const T, + T: ?Sized => *mut T, ); impl_known_layout!(const N: usize, T => [T; N]); safety_comment! { /// SAFETY: - /// `str` and `ManuallyDrop<[T]>` have the same representations as `[u8]` - /// and `[T]` repsectively. `str` has different bit validity than `[u8]`, - /// but that doesn't affect the soundness of this impl. + /// `str` and `ManuallyDrop<[T]>` [1] have the same representations as + /// `[u8]` and `[T]` repsectively. `str` has different bit validity than + /// `[u8]`, but that doesn't affect the soundness of this impl. + /// + /// [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html: + /// + /// `ManuallyDrop<T>` is guaranteed to have the same layout and bit + /// validity as `T` + /// + /// TODO(#429): + /// - Add quotes from docs. + /// - Once [1] (added in + /// https://github.com/rust-lang/rust/pull/115522) is available on stable, + /// quote the stable docs instead of the nightly docs. unsafe_impl_known_layout!(#[repr([u8])] str); unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ManuallyDrop<T>); } -/// Types for which a sequence of bytes all set to zero represents a valid -/// instance of the type. +/// Analyzes whether a type is [`FromZeroes`]. /// -/// WARNING: Do not implement this trait yourself! Instead, use -/// `#[derive(FromZeroes)]` (requires the `derive` Cargo feature). +/// This derive analyzes, at compile time, whether the annotated type satisfies +/// the [safety conditions] of `FromZeroes` and implements `FromZeroes` if it is +/// sound to do so. This derive can be applied to structs, enums, and unions; +/// e.g.: /// -/// Any memory region of the appropriate length which is guaranteed to contain -/// only zero bytes can be viewed as any `FromZeroes` type with no runtime -/// overhead. This is useful whenever memory is known to be in a zeroed state, -/// such memory returned from some allocation routines. +/// ``` +/// # use zerocopy_derive::FromZeroes; +/// #[derive(FromZeroes)] +/// struct MyStruct { +/// # /* +/// ... +/// # */ +/// } /// -/// `FromZeroes` is ignorant of byte order. For byte order-aware types, see the -/// [`byteorder`] module. +/// #[derive(FromZeroes)] +/// #[repr(u8)] +/// enum MyEnum { +/// # Variant0, +/// # /* +/// ... +/// # */ +/// } /// -/// # Safety +/// #[derive(FromZeroes)] +/// union MyUnion { +/// # variant: u8, +/// # /* +/// ... +/// # */ +/// } +/// ``` /// -/// *This section describes what is required in order for `T: FromZeroes`, and -/// what unsafe code may assume of such types. `#[derive(FromZeroes)]` only -/// permits types which satisfy these requirements. If you don't plan on -/// implementing `FromZeroes` manually, and you don't plan on writing unsafe -/// code that operates on `FromZeroes` types, then you don't need to read this -/// section.* +/// [safety conditions]: trait@FromZeroes#safety /// -/// If `T: FromZeroes`, then unsafe code may assume that: -/// - It is sound to treat any initialized sequence of zero bytes of length -/// `size_of::<T>()` as a `T`. -/// - Given `b: &[u8]` where `b.len() == size_of::<T>()`, `b` is aligned to -/// `align_of::<T>()`, and `b` contains only zero bytes, it is sound to -/// construct a `t: &T` at the same address as `b`, and it is sound for both -/// `b` and `t` to be live at the same time. +/// # Analysis /// -/// If a type is marked as `FromZeroes` which violates this contract, it may -/// cause undefined behavior. +/// *This section describes, roughly, the analysis performed by this derive to +/// determine whether it is sound to implement `FromZeroes` for a given type. +/// Unless you are modifying the implementation of this derive, or attempting to +/// manually implement `FromZeroes` for a type yourself, you don't need to read +/// this section.* /// -/// If a type has the following properties, then it is sound to implement +/// If a type has the following properties, then this derive can implement /// `FromZeroes` for that type: -/// - If the type is a struct, all of its fields must satisfy the requirements -/// to be `FromZeroes` (they do not actually have to be `FromZeroes`). +/// +/// - If the type is a struct, all of its fields must be `FromZeroes`. /// - If the type is an enum, it must be C-like (meaning that all variants have /// no fields) and it must have a variant with a discriminant of `0`. See [the /// reference] for a description of how discriminant values are chosen. @@ -581,13 +1050,18 @@ safety_comment! { /// (`FromZeroes` is not currently implemented for, e.g., /// `Option<&UnsafeCell<_>>`, but it could be one day). /// +/// This analysis is subject to change. Unsafe code may *only* rely on the +/// documented [safety conditions] of `FromZeroes`, and must *not* rely on the +/// implementation details of this derive. +/// /// [the reference]: https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations /// [`UnsafeCell`]: core::cell::UnsafeCell /// -/// # Rationale -/// /// ## Why isn't an explicit representation required for structs? /// +/// Neither this derive, nor the [safety conditions] of `FromZeroes`, requires +/// that structs are marked with `#[repr(C)]`. +/// /// Per the [Rust reference](reference), /// /// > The representation of a type can change the padding between fields, but @@ -611,6 +1085,270 @@ safety_comment! { /// its fields are `FromZeroes`. // TODO(#146): Document why we don't require an enum to have an explicit `repr` // attribute. +#[cfg(any(feature = "derive", test))] +#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] +pub use zerocopy_derive::FromZeroes; + +/// Types whose validity can be checked at runtime, allowing them to be +/// conditionally converted from byte slices. +/// +/// WARNING: Do not implement this trait yourself! Instead, use +/// `#[derive(TryFromBytes)]`. +/// +/// `TryFromBytes` types can safely be deserialized from an untrusted sequence +/// of bytes by performing a runtime check that the byte sequence contains a +/// valid instance of `Self`. +/// +/// `TryFromBytes` is ignorant of byte order. For byte order-aware types, see +/// the [`byteorder`] module. +/// +/// # What is a "valid instance"? +/// +/// In Rust, each type has *bit validity*, which refers to the set of bit +/// patterns which may appear in an instance of that type. It is impossible for +/// safe Rust code to produce values which violate bit validity (ie, values +/// outside of the "valid" set of bit patterns). If `unsafe` code produces an +/// invalid value, this is considered [undefined behavior]. +/// +/// Rust's bit validity rules are currently being decided, which means that some +/// types have three classes of bit patterns: those which are definitely valid, +/// and whose validity is documented in the language; those which may or may not +/// be considered valid at some point in the future; and those which are +/// definitely invalid. +/// +/// Zerocopy takes a conservative approach, and only considers a bit pattern to +/// be valid if its validity is a documenteed guarantee provided by the +/// language. +/// +/// For most use cases, Rust's current guarantees align with programmers' +/// intuitions about what ought to be valid. As a result, zerocopy's +/// conservatism should not affect most users. One notable exception is unions, +/// whose bit validity is very up in the air; zerocopy does not permit +/// implementing `TryFromBytes` for any union type. +/// +/// If you are negatively affected by lack of support for a particular type, +/// we encourage you to let us know by [filing an issue][github-repo]. +/// +/// # Safety +/// +/// On its own, `T: TryFromBytes` does not make any guarantees about the layout +/// or representation of `T`. It merely provides the ability to perform a +/// validity check at runtime via methods like [`try_from_ref`]. +/// +/// Currently, it is not possible to stably implement `TryFromBytes` other than +/// by using `#[derive(TryFromBytes)]`. While there are `#[doc(hidden)]` items +/// on this trait that provide well-defined safety invariants, no stability +/// guarantees are made with respect to these items. In particular, future +/// releases of zerocopy may make backwards-breaking changes to these items, +/// including changes that only affect soundness, which may cause code which +/// uses those items to silently become unsound. +/// +/// [undefined behavior]: https://raphlinus.github.io/programming/rust/2018/08/17/undefined-behavior.html +/// [github-repo]: https://github.com/google/zerocopy +/// [`try_from_ref`]: TryFromBytes::try_from_ref +// TODO(#5): Update `try_from_ref` doc link once it exists +#[doc(hidden)] +pub unsafe trait TryFromBytes { + /// Does a given memory range contain a valid instance of `Self`? + /// + /// # Safety + /// + /// ## Preconditions + /// + /// The memory referenced by `candidate` may only be accessed via reads for + /// the duration of this method call. This prohibits writes through mutable + /// references and through [`UnsafeCell`]s. There may exist immutable + /// references to the same memory which contain `UnsafeCell`s so long as: + /// - Those `UnsafeCell`s exist at the same byte ranges as `UnsafeCell`s in + /// `Self`. This is a bidirectional property: `Self` may not contain + /// `UnsafeCell`s where other references to the same memory do not, and + /// vice-versa. + /// - Those `UnsafeCell`s are never used to perform mutation for the + /// duration of this method call. + /// + /// The memory referenced by `candidate` may not be referenced by any + /// mutable references even if these references are not used to perform + /// mutation. + /// + /// `candidate` is not required to refer to a valid `Self`. However, it must + /// satisfy the requirement that uninitialized bytes may only be present + /// where it is possible for them to be present in `Self`. This is a dynamic + /// property: if, at a particular byte offset, a valid enum discriminant is + /// set, the subsequent bytes may only have uninitialized bytes as + /// specificed by the corresponding enum. + /// + /// Formally, given `len = size_of_val_raw(candidate)`, at every byte + /// offset, `b`, in the range `[0, len)`: + /// - If, in all instances `s: Self` of length `len`, the byte at offset `b` + /// in `s` is initialized, then the byte at offset `b` within `*candidate` + /// must be initialized. + /// - Let `c` be the contents of the byte range `[0, b)` in `*candidate`. + /// Let `S` be the subset of valid instances of `Self` of length `len` + /// which contain `c` in the offset range `[0, b)`. If, for all instances + /// of `s: Self` in `S`, the byte at offset `b` in `s` is initialized, + /// then the byte at offset `b` in `*candidate` must be initialized. + /// + /// Pragmatically, this means that if `*candidate` is guaranteed to + /// contain an enum type at a particular offset, and the enum discriminant + /// stored in `*candidate` corresponds to a valid variant of that enum + /// type, then it is guaranteed that the appropriate bytes of `*candidate` + /// are initialized as defined by that variant's bit validity (although + /// note that the variant may contain another enum type, in which case the + /// same rules apply depending on the state of its discriminant, and so on + /// recursively). + /// + /// ## Postconditions + /// + /// Unsafe code may assume that, if `is_bit_valid(candidate)` returns true, + /// `*candidate` contains a valid `Self`. + /// + /// # Panics + /// + /// `is_bit_valid` may panic. Callers are responsible for ensuring that any + /// `unsafe` code remains sound even in the face of `is_bit_valid` + /// panicking. (We support user-defined validation routines; so long as + /// these routines are not required to be `unsafe`, there is no way to + /// ensure that these do not generate panics.) + /// + /// [`UnsafeCell`]: core::cell::UnsafeCell + #[doc(hidden)] + unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool; + + /// Attempts to interpret a byte slice as a `Self`. + /// + /// `try_from_ref` validates that `bytes` contains a valid `Self`, and that + /// it satisfies `Self`'s alignment requirement. If it does, then `bytes` is + /// reinterpreted as a `Self`. + /// + /// Note that Rust's bit validity rules are still being decided. As such, + /// there exist types whose bit validity is ambiguous. See the + /// `TryFromBytes` docs for a discussion of how these cases are handled. + // TODO(#251): In a future in which we distinguish between `FromBytes` and + // `RefFromBytes`, this requires `where Self: RefFromBytes` to disallow + // interior mutability. + #[inline] + #[doc(hidden)] // TODO(#5): Finalize name before remove this attribute. + fn try_from_ref(bytes: &[u8]) -> Option<&Self> + where + Self: KnownLayout, + { + let maybe_self = Ptr::from(bytes).try_cast_into_no_leftover::<Self>()?; + + // SAFETY: + // - Since `bytes` is an immutable reference, we know that no mutable + // references exist to this memory region. + // - Since `[u8]` contains no `UnsafeCell`s, we know there are no + // `&UnsafeCell` references to this memory region. + // - Since we don't permit implementing `TryFromBytes` for types which + // contain `UnsafeCell`s, there are no `UnsafeCell`s in `Self`, and so + // the requirement that all references contain `UnsafeCell`s at the + // same offsets is trivially satisfied. + // - All bytes of `bytes` are initialized. + // + // This call may panic. If that happens, it doesn't cause any soundness + // issues, as we have not generated any invalid state which we need to + // fix before returning. + if unsafe { !Self::is_bit_valid(maybe_self) } { + return None; + } + + // SAFETY: + // - Preconditions for `as_ref`: + // - `is_bit_valid` guarantees that `*maybe_self` contains a valid + // `Self`. Since `&[u8]` does not permit interior mutation, this + // cannot be invalidated after this method returns. + // - Since the argument and return types are immutable references, + // Rust will prevent the caller from producing any mutable + // references to the same memory region. + // - Since `Self` is not allowed to contain any `UnsafeCell`s and the + // same is true of `[u8]`, interior mutation is not possible. Thus, + // no mutation is possible. For the same reason, there is no + // mismatch between the two types in terms of which byte ranges are + // referenced as `UnsafeCell`s. + // - Since interior mutation isn't possible within `Self`, there's no + // way for the returned reference to be used to modify the byte range, + // and thus there's no way for the returned reference to be used to + // write an invalid `[u8]` which would be observable via the original + // `&[u8]`. + Some(unsafe { maybe_self.as_ref() }) + } +} + +/// Types for which a sequence of bytes all set to zero represents a valid +/// instance of the type. +/// +/// Any memory region of the appropriate length which is guaranteed to contain +/// only zero bytes can be viewed as any `FromZeroes` type with no runtime +/// overhead. This is useful whenever memory is known to be in a zeroed state, +/// such memory returned from some allocation routines. +/// +/// # Implementation +/// +/// **Do not implement this trait yourself!** Instead, use +/// [`#[derive(FromZeroes)]`][derive] (requires the `derive` Cargo feature); +/// e.g.: +/// +/// ``` +/// # use zerocopy_derive::FromZeroes; +/// #[derive(FromZeroes)] +/// struct MyStruct { +/// # /* +/// ... +/// # */ +/// } +/// +/// #[derive(FromZeroes)] +/// #[repr(u8)] +/// enum MyEnum { +/// # Variant0, +/// # /* +/// ... +/// # */ +/// } +/// +/// #[derive(FromZeroes)] +/// union MyUnion { +/// # variant: u8, +/// # /* +/// ... +/// # */ +/// } +/// ``` +/// +/// This derive performs a sophisticated, compile-time safety analysis to +/// determine whether a type is `FromZeroes`. +/// +/// # Safety +/// +/// *This section describes what is required in order for `T: FromZeroes`, and +/// what unsafe code may assume of such types. If you don't plan on implementing +/// `FromZeroes` manually, and you don't plan on writing unsafe code that +/// operates on `FromZeroes` types, then you don't need to read this section.* +/// +/// If `T: FromZeroes`, then unsafe code may assume that: +/// - It is sound to treat any initialized sequence of zero bytes of length +/// `size_of::<T>()` as a `T`. +/// - Given `b: &[u8]` where `b.len() == size_of::<T>()`, `b` is aligned to +/// `align_of::<T>()`, and `b` contains only zero bytes, it is sound to +/// construct a `t: &T` at the same address as `b`, and it is sound for both +/// `b` and `t` to be live at the same time. +/// +/// If a type is marked as `FromZeroes` which violates this contract, it may +/// cause undefined behavior. +/// +/// `#[derive(FromZeroes)]` only permits [types which satisfy these +/// requirements][derive-analysis]. +/// +#[cfg_attr( + feature = "derive", + doc = "[derive]: zerocopy_derive::FromZeroes", + doc = "[derive-analysis]: zerocopy_derive::FromZeroes#analysis" +)] +#[cfg_attr( + not(feature = "derive"), + doc = concat!("[derive]: https://docs.rs/zerocopy/", "0.7.29", "/zerocopy/derive.FromZeroes.html"), + doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", "0.7.29", "/zerocopy/derive.FromZeroes.html#analysis"), +)] pub unsafe trait FromZeroes { // The `Self: Sized` bound makes it so that `FromZeroes` is still object // safe. @@ -625,6 +1363,36 @@ pub unsafe trait FromZeroes { /// Self::new_zeroed()`, it differs in that `zero` does not semantically /// drop the current value and replace it with a new one - it simply /// modifies the bytes of the existing value. + /// + /// # Examples + /// + /// ``` + /// # use zerocopy::FromZeroes; + /// # use zerocopy_derive::*; + /// # + /// #[derive(FromZeroes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// let mut header = PacketHeader { + /// src_port: 100u16.to_be_bytes(), + /// dst_port: 200u16.to_be_bytes(), + /// length: 300u16.to_be_bytes(), + /// checksum: 400u16.to_be_bytes(), + /// }; + /// + /// header.zero(); + /// + /// assert_eq!(header.src_port, [0, 0]); + /// assert_eq!(header.dst_port, [0, 0]); + /// assert_eq!(header.length, [0, 0]); + /// assert_eq!(header.checksum, [0, 0]); + /// ``` #[inline(always)] fn zero(&mut self) { let slf: *mut Self = self; @@ -636,10 +1404,35 @@ pub unsafe trait FromZeroes { // as required by `u8`. // - Since `Self: FromZeroes`, the all-zeroes instance is a valid // instance of `Self.` + // + // TODO(#429): Add references to docs and quotes. unsafe { ptr::write_bytes(slf.cast::<u8>(), 0, len) }; } /// Creates an instance of `Self` from zeroed bytes. + /// + /// # Examples + /// + /// ``` + /// # use zerocopy::FromZeroes; + /// # use zerocopy_derive::*; + /// # + /// #[derive(FromZeroes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// let header: PacketHeader = FromZeroes::new_zeroed(); + /// + /// assert_eq!(header.src_port, [0, 0]); + /// assert_eq!(header.dst_port, [0, 0]); + /// assert_eq!(header.length, [0, 0]); + /// assert_eq!(header.checksum, [0, 0]); + /// ``` #[inline(always)] fn new_zeroed() -> Self where @@ -668,6 +1461,7 @@ pub unsafe trait FromZeroes { /// /// Panics if allocation of `size_of::<Self>()` bytes fails. #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] fn new_box_zeroed() -> Box<Self> where @@ -680,13 +1474,15 @@ pub unsafe trait FromZeroes { return Box::new(Self::new_zeroed()); } - // TODO(#61): Add a "SAFETY" comment and remove this `allow`. + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. + #[allow(clippy::undocumented_unsafe_blocks)] + let ptr = unsafe { alloc::alloc::alloc_zeroed(layout).cast::<Self>() }; + if ptr.is_null() { + alloc::alloc::handle_alloc_error(layout); + } + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe { - let ptr = alloc::alloc::alloc_zeroed(layout).cast::<Self>(); - if ptr.is_null() { - alloc::alloc::handle_alloc_error(layout); - } Box::from_raw(ptr) } } @@ -713,6 +1509,7 @@ pub unsafe trait FromZeroes { /// * Panics if `size_of::<Self>() * len` overflows. /// * Panics if allocation of `size_of::<Self>() * len` bytes fails. #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] fn new_box_slice_zeroed(len: usize) -> Box<[Self]> where @@ -736,21 +1533,25 @@ pub unsafe trait FromZeroes { let layout = Layout::from_size_align(size, align).expect("total allocation size overflows `isize`"); - // TODO(#61): Add a "SAFETY" comment and remove this `allow`. + let ptr = if layout.size() != 0 { + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. + #[allow(clippy::undocumented_unsafe_blocks)] + let ptr = unsafe { alloc::alloc::alloc_zeroed(layout).cast::<Self>() }; + if ptr.is_null() { + alloc::alloc::handle_alloc_error(layout); + } + ptr + } else { + // `Box<[T]>` does not allocate when `T` is zero-sized or when `len` + // is zero, but it does require a non-null dangling pointer for its + // allocation. + NonNull::<Self>::dangling().as_ptr() + }; + + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe { - if layout.size() != 0 { - let ptr = alloc::alloc::alloc_zeroed(layout).cast::<Self>(); - if ptr.is_null() { - alloc::alloc::handle_alloc_error(layout); - } - Box::from_raw(slice::from_raw_parts_mut(ptr, len)) - } else { - // `Box<[T]>` does not allocate when `T` is zero-sized or when - // `len` is zero, but it does require a non-null dangling - // pointer for its allocation. - Box::from_raw(slice::from_raw_parts_mut(NonNull::<Self>::dangling().as_ptr(), len)) - } + Box::from_raw(slice::from_raw_parts_mut(ptr, len)) } } @@ -775,6 +1576,7 @@ pub unsafe trait FromZeroes { /// * Panics if `size_of::<Self>() * len` overflows. /// * Panics if allocation of `size_of::<Self>() * len` bytes fails. #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "new_vec_zeroed")))] #[inline(always)] fn new_vec_zeroed(len: usize) -> Vec<Self> where @@ -784,40 +1586,71 @@ pub unsafe trait FromZeroes { } } -/// Types for which any byte pattern is valid. +/// Analyzes whether a type is [`FromBytes`]. /// -/// WARNING: Do not implement this trait yourself! Instead, use -/// `#[derive(FromBytes)]` (requires the `derive` Cargo feature). +/// This derive analyzes, at compile time, whether the annotated type satisfies +/// the [safety conditions] of `FromBytes` and implements `FromBytes` if it is +/// sound to do so. This derive can be applied to structs, enums, and unions; +/// e.g.: /// -/// `FromBytes` types can safely be deserialized from an untrusted sequence of -/// bytes because any byte sequence corresponds to a valid instance of the type. +/// ``` +/// # use zerocopy_derive::{FromBytes, FromZeroes}; +/// #[derive(FromZeroes, FromBytes)] +/// struct MyStruct { +/// # /* +/// ... +/// # */ +/// } /// -/// `FromBytes` is ignorant of byte order. For byte order-aware types, see the -/// [`byteorder`] module. +/// #[derive(FromZeroes, FromBytes)] +/// #[repr(u8)] +/// enum MyEnum { +/// # V00, V01, V02, V03, V04, V05, V06, V07, V08, V09, V0A, V0B, V0C, V0D, V0E, +/// # V0F, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V1A, V1B, V1C, V1D, +/// # V1E, V1F, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V2A, V2B, V2C, +/// # V2D, V2E, V2F, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V3A, V3B, +/// # V3C, V3D, V3E, V3F, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V4A, +/// # V4B, V4C, V4D, V4E, V4F, V50, V51, V52, V53, V54, V55, V56, V57, V58, V59, +/// # V5A, V5B, V5C, V5D, V5E, V5F, V60, V61, V62, V63, V64, V65, V66, V67, V68, +/// # V69, V6A, V6B, V6C, V6D, V6E, V6F, V70, V71, V72, V73, V74, V75, V76, V77, +/// # V78, V79, V7A, V7B, V7C, V7D, V7E, V7F, V80, V81, V82, V83, V84, V85, V86, +/// # V87, V88, V89, V8A, V8B, V8C, V8D, V8E, V8F, V90, V91, V92, V93, V94, V95, +/// # V96, V97, V98, V99, V9A, V9B, V9C, V9D, V9E, V9F, VA0, VA1, VA2, VA3, VA4, +/// # VA5, VA6, VA7, VA8, VA9, VAA, VAB, VAC, VAD, VAE, VAF, VB0, VB1, VB2, VB3, +/// # VB4, VB5, VB6, VB7, VB8, VB9, VBA, VBB, VBC, VBD, VBE, VBF, VC0, VC1, VC2, +/// # VC3, VC4, VC5, VC6, VC7, VC8, VC9, VCA, VCB, VCC, VCD, VCE, VCF, VD0, VD1, +/// # VD2, VD3, VD4, VD5, VD6, VD7, VD8, VD9, VDA, VDB, VDC, VDD, VDE, VDF, VE0, +/// # VE1, VE2, VE3, VE4, VE5, VE6, VE7, VE8, VE9, VEA, VEB, VEC, VED, VEE, VEF, +/// # VF0, VF1, VF2, VF3, VF4, VF5, VF6, VF7, VF8, VF9, VFA, VFB, VFC, VFD, VFE, +/// # VFF, +/// # /* +/// ... +/// # */ +/// } /// -/// # Safety +/// #[derive(FromZeroes, FromBytes)] +/// union MyUnion { +/// # variant: u8, +/// # /* +/// ... +/// # */ +/// } +/// ``` /// -/// *This section describes what is required in order for `T: FromBytes`, and -/// what unsafe code may assume of such types. `#[derive(FromBytes)]` only -/// permits types which satisfy these requirements. If you don't plan on -/// implementing `FromBytes` manually, and you don't plan on writing unsafe code -/// that operates on `FromBytes` types, then you don't need to read this -/// section.* +/// [safety conditions]: trait@FromBytes#safety /// -/// If `T: FromBytes`, then unsafe code may assume that: -/// - It is sound to treat any initialized sequence of bytes of length -/// `size_of::<T>()` as a `T`. -/// - Given `b: &[u8]` where `b.len() == size_of::<T>()` and `b` is aligned to -/// `align_of::<T>()`, it is sound to construct a `t: &T` at the same address -/// as `b`, and it is sound for both `b` and `t` to be live at the same time. +/// # Analysis /// -/// If a type is marked as `FromBytes` which violates this contract, it may -/// cause undefined behavior. +/// *This section describes, roughly, the analysis performed by this derive to +/// determine whether it is sound to implement `FromBytes` for a given type. +/// Unless you are modifying the implementation of this derive, or attempting to +/// manually implement `FromBytes` for a type yourself, you don't need to read +/// this section.* /// -/// If a type has the following properties, then it is sound to implement +/// If a type has the following properties, then this derive can implement /// `FromBytes` for that type: -/// - If the type is a struct, all of its fields must satisfy the requirements -/// to be `FromBytes` (they do not actually have to be `FromBytes`) +/// +/// - If the type is a struct, all of its fields must be `FromBytes`. /// - If the type is an enum: /// - It must be a C-like enum (meaning that all variants have no fields). /// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`, @@ -835,10 +1668,15 @@ pub unsafe trait FromZeroes { /// /// [`UnsafeCell`]: core::cell::UnsafeCell /// -/// # Rationale +/// This analysis is subject to change. Unsafe code may *only* rely on the +/// documented [safety conditions] of `FromBytes`, and must *not* rely on the +/// implementation details of this derive. /// /// ## Why isn't an explicit representation required for structs? /// +/// Neither this derive, nor the [safety conditions] of `FromBytes`, requires +/// that structs are marked with `#[repr(C)]`. +/// /// Per the [Rust reference](reference), /// /// > The representation of a type can change the padding between fields, but @@ -860,6 +1698,101 @@ pub unsafe trait FromZeroes { /// /// Whether a struct is soundly `FromBytes` therefore solely depends on whether /// its fields are `FromBytes`. +// TODO(#146): Document why we don't require an enum to have an explicit `repr` +// attribute. +#[cfg(any(feature = "derive", test))] +#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] +pub use zerocopy_derive::FromBytes; + +/// Types for which any bit pattern is valid. +/// +/// Any memory region of the appropriate length which contains initialized bytes +/// can be viewed as any `FromBytes` type with no runtime overhead. This is +/// useful for efficiently parsing bytes as structured data. +/// +/// # Implementation +/// +/// **Do not implement this trait yourself!** Instead, use +/// [`#[derive(FromBytes)]`][derive] (requires the `derive` Cargo feature); +/// e.g.: +/// +/// ``` +/// # use zerocopy_derive::{FromBytes, FromZeroes}; +/// #[derive(FromZeroes, FromBytes)] +/// struct MyStruct { +/// # /* +/// ... +/// # */ +/// } +/// +/// #[derive(FromZeroes, FromBytes)] +/// #[repr(u8)] +/// enum MyEnum { +/// # V00, V01, V02, V03, V04, V05, V06, V07, V08, V09, V0A, V0B, V0C, V0D, V0E, +/// # V0F, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V1A, V1B, V1C, V1D, +/// # V1E, V1F, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V2A, V2B, V2C, +/// # V2D, V2E, V2F, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V3A, V3B, +/// # V3C, V3D, V3E, V3F, V40, V41, V42, V43, V44, V45, V46, V47, V48, V49, V4A, +/// # V4B, V4C, V4D, V4E, V4F, V50, V51, V52, V53, V54, V55, V56, V57, V58, V59, +/// # V5A, V5B, V5C, V5D, V5E, V5F, V60, V61, V62, V63, V64, V65, V66, V67, V68, +/// # V69, V6A, V6B, V6C, V6D, V6E, V6F, V70, V71, V72, V73, V74, V75, V76, V77, +/// # V78, V79, V7A, V7B, V7C, V7D, V7E, V7F, V80, V81, V82, V83, V84, V85, V86, +/// # V87, V88, V89, V8A, V8B, V8C, V8D, V8E, V8F, V90, V91, V92, V93, V94, V95, +/// # V96, V97, V98, V99, V9A, V9B, V9C, V9D, V9E, V9F, VA0, VA1, VA2, VA3, VA4, +/// # VA5, VA6, VA7, VA8, VA9, VAA, VAB, VAC, VAD, VAE, VAF, VB0, VB1, VB2, VB3, +/// # VB4, VB5, VB6, VB7, VB8, VB9, VBA, VBB, VBC, VBD, VBE, VBF, VC0, VC1, VC2, +/// # VC3, VC4, VC5, VC6, VC7, VC8, VC9, VCA, VCB, VCC, VCD, VCE, VCF, VD0, VD1, +/// # VD2, VD3, VD4, VD5, VD6, VD7, VD8, VD9, VDA, VDB, VDC, VDD, VDE, VDF, VE0, +/// # VE1, VE2, VE3, VE4, VE5, VE6, VE7, VE8, VE9, VEA, VEB, VEC, VED, VEE, VEF, +/// # VF0, VF1, VF2, VF3, VF4, VF5, VF6, VF7, VF8, VF9, VFA, VFB, VFC, VFD, VFE, +/// # VFF, +/// # /* +/// ... +/// # */ +/// } +/// +/// #[derive(FromZeroes, FromBytes)] +/// union MyUnion { +/// # variant: u8, +/// # /* +/// ... +/// # */ +/// } +/// ``` +/// +/// This derive performs a sophisticated, compile-time safety analysis to +/// determine whether a type is `FromBytes`. +/// +/// # Safety +/// +/// *This section describes what is required in order for `T: FromBytes`, and +/// what unsafe code may assume of such types. If you don't plan on implementing +/// `FromBytes` manually, and you don't plan on writing unsafe code that +/// operates on `FromBytes` types, then you don't need to read this section.* +/// +/// If `T: FromBytes`, then unsafe code may assume that: +/// - It is sound to treat any initialized sequence of bytes of length +/// `size_of::<T>()` as a `T`. +/// - Given `b: &[u8]` where `b.len() == size_of::<T>()`, `b` is aligned to +/// `align_of::<T>()` it is sound to construct a `t: &T` at the same address +/// as `b`, and it is sound for both `b` and `t` to be live at the same time. +/// +/// If a type is marked as `FromBytes` which violates this contract, it may +/// cause undefined behavior. +/// +/// `#[derive(FromBytes)]` only permits [types which satisfy these +/// requirements][derive-analysis]. +/// +#[cfg_attr( + feature = "derive", + doc = "[derive]: zerocopy_derive::FromBytes", + doc = "[derive-analysis]: zerocopy_derive::FromBytes#analysis" +)] +#[cfg_attr( + not(feature = "derive"), + doc = concat!("[derive]: https://docs.rs/zerocopy/", "0.7.29", "/zerocopy/derive.FromBytes.html"), + doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", "0.7.29", "/zerocopy/derive.FromBytes.html#analysis"), +)] pub unsafe trait FromBytes: FromZeroes { // The `Self: Sized` bound makes it so that `FromBytes` is still object // safe. @@ -868,16 +1801,596 @@ pub unsafe trait FromBytes: FromZeroes { where Self: Sized; + /// Interprets the given `bytes` as a `&Self` without copying. + /// + /// If `bytes.len() != size_of::<Self>()` or `bytes` is not aligned to + /// `align_of::<Self>()`, this returns `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// // These bytes encode a `PacketHeader`. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7].as_slice(); + /// + /// let header = PacketHeader::ref_from(bytes).unwrap(); + /// + /// assert_eq!(header.src_port, [0, 1]); + /// assert_eq!(header.dst_port, [2, 3]); + /// assert_eq!(header.length, [4, 5]); + /// assert_eq!(header.checksum, [6, 7]); + /// ``` + #[inline] + fn ref_from(bytes: &[u8]) -> Option<&Self> + where + Self: Sized, + { + Ref::<&[u8], Self>::new(bytes).map(Ref::into_ref) + } + + /// Interprets the prefix of the given `bytes` as a `&Self` without copying. + /// + /// `ref_from_prefix` returns a reference to the first `size_of::<Self>()` + /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()` or `bytes` is not + /// aligned to `align_of::<Self>()`, this returns `None`. + /// + /// To also access the prefix bytes, use [`Ref::new_from_prefix`]. Then, use + /// [`Ref::into_ref`] to get a `&Self` with the same lifetime. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// // These are more bytes than are needed to encode a `PacketHeader`. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_slice(); + /// + /// let header = PacketHeader::ref_from_prefix(bytes).unwrap(); + /// + /// assert_eq!(header.src_port, [0, 1]); + /// assert_eq!(header.dst_port, [2, 3]); + /// assert_eq!(header.length, [4, 5]); + /// assert_eq!(header.checksum, [6, 7]); + /// ``` + #[inline] + fn ref_from_prefix(bytes: &[u8]) -> Option<&Self> + where + Self: Sized, + { + Ref::<&[u8], Self>::new_from_prefix(bytes).map(|(r, _)| r.into_ref()) + } + + /// Interprets the suffix of the given `bytes` as a `&Self` without copying. + /// + /// `ref_from_suffix` returns a reference to the last `size_of::<Self>()` + /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()` or the suffix of + /// `bytes` is not aligned to `align_of::<Self>()`, this returns `None`. + /// + /// To also access the suffix bytes, use [`Ref::new_from_suffix`]. Then, use + /// [`Ref::into_ref`] to get a `&Self` with the same lifetime. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketTrailer { + /// frame_check_sequence: [u8; 4], + /// } + /// + /// // These are more bytes than are needed to encode a `PacketTrailer`. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_slice(); + /// + /// let trailer = PacketTrailer::ref_from_suffix(bytes).unwrap(); + /// + /// assert_eq!(trailer.frame_check_sequence, [6, 7, 8, 9]); + /// ``` + #[inline] + fn ref_from_suffix(bytes: &[u8]) -> Option<&Self> + where + Self: Sized, + { + Ref::<&[u8], Self>::new_from_suffix(bytes).map(|(_, r)| r.into_ref()) + } + + /// Interprets the given `bytes` as a `&mut Self` without copying. + /// + /// If `bytes.len() != size_of::<Self>()` or `bytes` is not aligned to + /// `align_of::<Self>()`, this returns `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(AsBytes, FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// // These bytes encode a `PacketHeader`. + /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7][..]; + /// + /// let header = PacketHeader::mut_from(bytes).unwrap(); + /// + /// assert_eq!(header.src_port, [0, 1]); + /// assert_eq!(header.dst_port, [2, 3]); + /// assert_eq!(header.length, [4, 5]); + /// assert_eq!(header.checksum, [6, 7]); + /// + /// header.checksum = [0, 0]; + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0]); + /// ``` + #[inline] + fn mut_from(bytes: &mut [u8]) -> Option<&mut Self> + where + Self: Sized + AsBytes, + { + Ref::<&mut [u8], Self>::new(bytes).map(Ref::into_mut) + } + + /// Interprets the prefix of the given `bytes` as a `&mut Self` without + /// copying. + /// + /// `mut_from_prefix` returns a reference to the first `size_of::<Self>()` + /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()` or `bytes` is not + /// aligned to `align_of::<Self>()`, this returns `None`. + /// + /// To also access the prefix bytes, use [`Ref::new_from_prefix`]. Then, use + /// [`Ref::into_mut`] to get a `&mut Self` with the same lifetime. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(AsBytes, FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// // These are more bytes than are needed to encode a `PacketHeader`. + /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; + /// + /// let header = PacketHeader::mut_from_prefix(bytes).unwrap(); + /// + /// assert_eq!(header.src_port, [0, 1]); + /// assert_eq!(header.dst_port, [2, 3]); + /// assert_eq!(header.length, [4, 5]); + /// assert_eq!(header.checksum, [6, 7]); + /// + /// header.checksum = [0, 0]; + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0, 8, 9]); + /// ``` + #[inline] + fn mut_from_prefix(bytes: &mut [u8]) -> Option<&mut Self> + where + Self: Sized + AsBytes, + { + Ref::<&mut [u8], Self>::new_from_prefix(bytes).map(|(r, _)| r.into_mut()) + } + + /// Interprets the suffix of the given `bytes` as a `&mut Self` without copying. + /// + /// `mut_from_suffix` returns a reference to the last `size_of::<Self>()` + /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()` or the suffix of + /// `bytes` is not aligned to `align_of::<Self>()`, this returns `None`. + /// + /// To also access the suffix bytes, use [`Ref::new_from_suffix`]. Then, + /// use [`Ref::into_mut`] to get a `&mut Self` with the same lifetime. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(AsBytes, FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketTrailer { + /// frame_check_sequence: [u8; 4], + /// } + /// + /// // These are more bytes than are needed to encode a `PacketTrailer`. + /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; + /// + /// let trailer = PacketTrailer::mut_from_suffix(bytes).unwrap(); + /// + /// assert_eq!(trailer.frame_check_sequence, [6, 7, 8, 9]); + /// + /// trailer.frame_check_sequence = [0, 0, 0, 0]; + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0, 0, 0]); + /// ``` + #[inline] + fn mut_from_suffix(bytes: &mut [u8]) -> Option<&mut Self> + where + Self: Sized + AsBytes, + { + Ref::<&mut [u8], Self>::new_from_suffix(bytes).map(|(_, r)| r.into_mut()) + } + + /// Interprets the given `bytes` as a `&[Self]` without copying. + /// + /// If `bytes.len() % size_of::<Self>() != 0` or `bytes` is not aligned to + /// `align_of::<Self>()`, this returns `None`. + /// + /// If you need to convert a specific number of slice elements, see + /// [`slice_from_prefix`](FromBytes::slice_from_prefix) or + /// [`slice_from_suffix`](FromBytes::slice_from_suffix). + /// + /// # Panics + /// + /// If `Self` is a zero-sized type. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// # #[derive(Debug, PartialEq, Eq)] + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct Pixel { + /// r: u8, + /// g: u8, + /// b: u8, + /// a: u8, + /// } + /// + /// // These bytes encode two `Pixel`s. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7].as_slice(); + /// + /// let pixels = Pixel::slice_from(bytes).unwrap(); + /// + /// assert_eq!(pixels, &[ + /// Pixel { r: 0, g: 1, b: 2, a: 3 }, + /// Pixel { r: 4, g: 5, b: 6, a: 7 }, + /// ]); + /// ``` + #[inline] + fn slice_from(bytes: &[u8]) -> Option<&[Self]> + where + Self: Sized, + { + Ref::<_, [Self]>::new_slice(bytes).map(|r| r.into_slice()) + } + + /// Interprets the prefix of the given `bytes` as a `&[Self]` with length + /// equal to `count` without copying. + /// + /// This method verifies that `bytes.len() >= size_of::<T>() * count` + /// and that `bytes` is aligned to `align_of::<T>()`. It consumes the + /// first `size_of::<T>() * count` bytes from `bytes` to construct a + /// `&[Self]`, and returns the remaining bytes to the caller. It also + /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. + /// If any of the length, alignment, or overflow checks fail, it returns + /// `None`. + /// + /// # Panics + /// + /// If `T` is a zero-sized type. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// # #[derive(Debug, PartialEq, Eq)] + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct Pixel { + /// r: u8, + /// g: u8, + /// b: u8, + /// a: u8, + /// } + /// + /// // These are more bytes than are needed to encode two `Pixel`s. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_slice(); + /// + /// let (pixels, rest) = Pixel::slice_from_prefix(bytes, 2).unwrap(); + /// + /// assert_eq!(pixels, &[ + /// Pixel { r: 0, g: 1, b: 2, a: 3 }, + /// Pixel { r: 4, g: 5, b: 6, a: 7 }, + /// ]); + /// + /// assert_eq!(rest, &[8, 9]); + /// ``` + #[inline] + fn slice_from_prefix(bytes: &[u8], count: usize) -> Option<(&[Self], &[u8])> + where + Self: Sized, + { + Ref::<_, [Self]>::new_slice_from_prefix(bytes, count).map(|(r, b)| (r.into_slice(), b)) + } + + /// Interprets the suffix of the given `bytes` as a `&[Self]` with length + /// equal to `count` without copying. + /// + /// This method verifies that `bytes.len() >= size_of::<T>() * count` + /// and that `bytes` is aligned to `align_of::<T>()`. It consumes the + /// last `size_of::<T>() * count` bytes from `bytes` to construct a + /// `&[Self]`, and returns the preceding bytes to the caller. It also + /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. + /// If any of the length, alignment, or overflow checks fail, it returns + /// `None`. + /// + /// # Panics + /// + /// If `T` is a zero-sized type. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// # #[derive(Debug, PartialEq, Eq)] + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct Pixel { + /// r: u8, + /// g: u8, + /// b: u8, + /// a: u8, + /// } + /// + /// // These are more bytes than are needed to encode two `Pixel`s. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_slice(); + /// + /// let (rest, pixels) = Pixel::slice_from_suffix(bytes, 2).unwrap(); + /// + /// assert_eq!(rest, &[0, 1]); + /// + /// assert_eq!(pixels, &[ + /// Pixel { r: 2, g: 3, b: 4, a: 5 }, + /// Pixel { r: 6, g: 7, b: 8, a: 9 }, + /// ]); + /// ``` + #[inline] + fn slice_from_suffix(bytes: &[u8], count: usize) -> Option<(&[u8], &[Self])> + where + Self: Sized, + { + Ref::<_, [Self]>::new_slice_from_suffix(bytes, count).map(|(b, r)| (b, r.into_slice())) + } + + /// Interprets the given `bytes` as a `&mut [Self]` without copying. + /// + /// If `bytes.len() % size_of::<T>() != 0` or `bytes` is not aligned to + /// `align_of::<T>()`, this returns `None`. + /// + /// If you need to convert a specific number of slice elements, see + /// [`mut_slice_from_prefix`](FromBytes::mut_slice_from_prefix) or + /// [`mut_slice_from_suffix`](FromBytes::mut_slice_from_suffix). + /// + /// # Panics + /// + /// If `T` is a zero-sized type. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// # #[derive(Debug, PartialEq, Eq)] + /// #[derive(AsBytes, FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct Pixel { + /// r: u8, + /// g: u8, + /// b: u8, + /// a: u8, + /// } + /// + /// // These bytes encode two `Pixel`s. + /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7][..]; + /// + /// let pixels = Pixel::mut_slice_from(bytes).unwrap(); + /// + /// assert_eq!(pixels, &[ + /// Pixel { r: 0, g: 1, b: 2, a: 3 }, + /// Pixel { r: 4, g: 5, b: 6, a: 7 }, + /// ]); + /// + /// pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 }; + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 0, 0, 0, 0]); + /// ``` + #[inline] + fn mut_slice_from(bytes: &mut [u8]) -> Option<&mut [Self]> + where + Self: Sized + AsBytes, + { + Ref::<_, [Self]>::new_slice(bytes).map(|r| r.into_mut_slice()) + } + + /// Interprets the prefix of the given `bytes` as a `&mut [Self]` with length + /// equal to `count` without copying. + /// + /// This method verifies that `bytes.len() >= size_of::<T>() * count` + /// and that `bytes` is aligned to `align_of::<T>()`. It consumes the + /// first `size_of::<T>() * count` bytes from `bytes` to construct a + /// `&[Self]`, and returns the remaining bytes to the caller. It also + /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. + /// If any of the length, alignment, or overflow checks fail, it returns + /// `None`. + /// + /// # Panics + /// + /// If `T` is a zero-sized type. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// # #[derive(Debug, PartialEq, Eq)] + /// #[derive(AsBytes, FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct Pixel { + /// r: u8, + /// g: u8, + /// b: u8, + /// a: u8, + /// } + /// + /// // These are more bytes than are needed to encode two `Pixel`s. + /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; + /// + /// let (pixels, rest) = Pixel::mut_slice_from_prefix(bytes, 2).unwrap(); + /// + /// assert_eq!(pixels, &[ + /// Pixel { r: 0, g: 1, b: 2, a: 3 }, + /// Pixel { r: 4, g: 5, b: 6, a: 7 }, + /// ]); + /// + /// assert_eq!(rest, &[8, 9]); + /// + /// pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 }; + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 0, 0, 0, 0, 8, 9]); + /// ``` + #[inline] + fn mut_slice_from_prefix(bytes: &mut [u8], count: usize) -> Option<(&mut [Self], &mut [u8])> + where + Self: Sized + AsBytes, + { + Ref::<_, [Self]>::new_slice_from_prefix(bytes, count).map(|(r, b)| (r.into_mut_slice(), b)) + } + + /// Interprets the suffix of the given `bytes` as a `&mut [Self]` with length + /// equal to `count` without copying. + /// + /// This method verifies that `bytes.len() >= size_of::<T>() * count` + /// and that `bytes` is aligned to `align_of::<T>()`. It consumes the + /// last `size_of::<T>() * count` bytes from `bytes` to construct a + /// `&[Self]`, and returns the preceding bytes to the caller. It also + /// ensures that `sizeof::<T>() * count` does not overflow a `usize`. + /// If any of the length, alignment, or overflow checks fail, it returns + /// `None`. + /// + /// # Panics + /// + /// If `T` is a zero-sized type. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// # #[derive(Debug, PartialEq, Eq)] + /// #[derive(AsBytes, FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct Pixel { + /// r: u8, + /// g: u8, + /// b: u8, + /// a: u8, + /// } + /// + /// // These are more bytes than are needed to encode two `Pixel`s. + /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]; + /// + /// let (rest, pixels) = Pixel::mut_slice_from_suffix(bytes, 2).unwrap(); + /// + /// assert_eq!(rest, &[0, 1]); + /// + /// assert_eq!(pixels, &[ + /// Pixel { r: 2, g: 3, b: 4, a: 5 }, + /// Pixel { r: 6, g: 7, b: 8, a: 9 }, + /// ]); + /// + /// pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 }; + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0, 0, 0]); + /// ``` + #[inline] + fn mut_slice_from_suffix(bytes: &mut [u8], count: usize) -> Option<(&mut [u8], &mut [Self])> + where + Self: Sized + AsBytes, + { + Ref::<_, [Self]>::new_slice_from_suffix(bytes, count).map(|(b, r)| (b, r.into_mut_slice())) + } + /// Reads a copy of `Self` from `bytes`. /// /// If `bytes.len() != size_of::<Self>()`, `read_from` returns `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// // These bytes encode a `PacketHeader`. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7].as_slice(); + /// + /// let header = PacketHeader::read_from(bytes).unwrap(); + /// + /// assert_eq!(header.src_port, [0, 1]); + /// assert_eq!(header.dst_port, [2, 3]); + /// assert_eq!(header.length, [4, 5]); + /// assert_eq!(header.checksum, [6, 7]); + /// ``` #[inline] fn read_from(bytes: &[u8]) -> Option<Self> where Self: Sized, { - let r = Ref::<_, Unalign<Self>>::new_unaligned(bytes)?; - Some(r.read().into_inner()) + Ref::<_, Unalign<Self>>::new_unaligned(bytes).map(|r| r.read().into_inner()) } /// Reads a copy of `Self` from the prefix of `bytes`. @@ -885,13 +2398,39 @@ pub unsafe trait FromBytes: FromZeroes { /// `read_from_prefix` reads a `Self` from the first `size_of::<Self>()` /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()`, it returns /// `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// // These are more bytes than are needed to encode a `PacketHeader`. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_slice(); + /// + /// let header = PacketHeader::read_from_prefix(bytes).unwrap(); + /// + /// assert_eq!(header.src_port, [0, 1]); + /// assert_eq!(header.dst_port, [2, 3]); + /// assert_eq!(header.length, [4, 5]); + /// assert_eq!(header.checksum, [6, 7]); + /// ``` #[inline] fn read_from_prefix(bytes: &[u8]) -> Option<Self> where Self: Sized, { - let (r, _suffix) = Ref::<_, Unalign<Self>>::new_unaligned_from_prefix(bytes)?; - Some(r.read().into_inner()) + Ref::<_, Unalign<Self>>::new_unaligned_from_prefix(bytes) + .map(|(r, _)| r.read().into_inner()) } /// Reads a copy of `Self` from the suffix of `bytes`. @@ -899,31 +2438,75 @@ pub unsafe trait FromBytes: FromZeroes { /// `read_from_suffix` reads a `Self` from the last `size_of::<Self>()` /// bytes of `bytes`. If `bytes.len() < size_of::<Self>()`, it returns /// `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::FromBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketTrailer { + /// frame_check_sequence: [u8; 4], + /// } + /// + /// // These are more bytes than are needed to encode a `PacketTrailer`. + /// let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_slice(); + /// + /// let trailer = PacketTrailer::read_from_suffix(bytes).unwrap(); + /// + /// assert_eq!(trailer.frame_check_sequence, [6, 7, 8, 9]); + /// ``` #[inline] fn read_from_suffix(bytes: &[u8]) -> Option<Self> where Self: Sized, { - let (_prefix, r) = Ref::<_, Unalign<Self>>::new_unaligned_from_suffix(bytes)?; - Some(r.read().into_inner()) + Ref::<_, Unalign<Self>>::new_unaligned_from_suffix(bytes) + .map(|(_, r)| r.read().into_inner()) } } -/// Types which are safe to treat as an immutable byte slice. +/// Analyzes whether a type is [`AsBytes`]. /// -/// WARNING: Do not implement this trait yourself! Instead, use -/// `#[derive(AsBytes)]` (requires the `derive` Cargo feature). +/// This derive analyzes, at compile time, whether the annotated type satisfies +/// the [safety conditions] of `AsBytes` and implements `AsBytes` if it is +/// sound to do so. This derive can be applied to structs, enums, and unions; +/// e.g.: +/// +/// ``` +/// # use zerocopy_derive::{AsBytes}; +/// #[derive(AsBytes)] +/// #[repr(C)] +/// struct MyStruct { +/// # /* +/// ... +/// # */ +/// } /// -/// `AsBytes` types can be safely viewed as a slice of bytes. In particular, -/// this means that, in any valid instance of the type, none of the bytes of the -/// instance are uninitialized. This precludes the following types: -/// - Structs with internal padding -/// - Unions in which not all variants have the same length +/// #[derive(AsBytes)] +/// #[repr(u8)] +/// enum MyEnum { +/// # Variant, +/// # /* +/// ... +/// # */ +/// } /// -/// `AsBytes` is ignorant of byte order. For byte order-aware types, see the -/// [`byteorder`] module. +/// #[derive(AsBytes)] +/// #[repr(C)] +/// union MyUnion { +/// # variant: u8, +/// # /* +/// ... +/// # */ +/// } +/// ``` /// -/// # Custom Derive Errors +/// [safety conditions]: trait@AsBytes#safety +/// +/// # Error Messages /// /// Due to the way that the custom derive for `AsBytes` is implemented, you may /// get an error like this: @@ -942,34 +2525,26 @@ pub unsafe trait FromBytes: FromZeroes { /// is illegal for `AsBytes` types. Consider reducing the alignment of some /// fields by using types in the [`byteorder`] module, adding explicit struct /// fields where those padding bytes would be, or using `#[repr(packed)]`. See -/// the Rust Reference's [page on type layout](type-layout) for more information +/// the Rust Reference's page on [type layout] for more information /// about type layout and padding. /// -/// # Safety -/// -/// *This section describes what is required in order for `T: AsBytes`, and what -/// unsafe code may assume of such types. `#[derive(AsBytes)]` only permits -/// types which satisfy these requirements. If you don't plan on implementing -/// `AsBytes` manually, and you don't plan on writing unsafe code that operates -/// on `AsBytes` types, then you don't need to read this section.* +/// [type layout]: https://doc.rust-lang.org/reference/type-layout.html /// -/// If `T: AsBytes`, then unsafe code may assume that: -/// - It is sound to treat any `t: T` as an immutable `[u8]` of length -/// `size_of_val(t)`. -/// - Given `t: &T`, it is sound to construct a `b: &[u8]` where `b.len() == -/// size_of_val(t)` at the same address as `t`, and it is sound for both `b` -/// and `t` to be live at the same time. +/// # Analysis /// -/// If a type is marked as `AsBytes` which violates this contract, it may cause -/// undefined behavior. +/// *This section describes, roughly, the analysis performed by this derive to +/// determine whether it is sound to implement `AsBytes` for a given type. +/// Unless you are modifying the implementation of this derive, or attempting to +/// manually implement `AsBytes` for a type yourself, you don't need to read +/// this section.* /// -/// If a type has the following properties, then it is sound to implement +/// If a type has the following properties, then this derive can implement /// `AsBytes` for that type: +/// /// - If the type is a struct: /// - It must have a defined representation (`repr(C)`, `repr(transparent)`, /// or `repr(packed)`). -/// - All of its fields must satisfy the requirements to be `AsBytes` (they do -/// not actually have to be `AsBytes`). +/// - All of its fields must be `AsBytes`. /// - Its layout must have no padding. This is always true for /// `repr(transparent)` and `repr(packed)`. For `repr(C)`, see the layout /// algorithm described in the [Rust Reference]. @@ -984,9 +2559,92 @@ pub unsafe trait FromBytes: FromZeroes { /// is not currently implemented for, e.g., `Option<&UnsafeCell<_>>`, but it /// could be one day). /// -/// [type-layout]: https://doc.rust-lang.org/reference/type-layout.html -/// [Rust Reference]: https://doc.rust-lang.org/reference/type-layout.html /// [`UnsafeCell`]: core::cell::UnsafeCell +/// +/// This analysis is subject to change. Unsafe code may *only* rely on the +/// documented [safety conditions] of `FromBytes`, and must *not* rely on the +/// implementation details of this derive. +/// +/// [Rust Reference]: https://doc.rust-lang.org/reference/type-layout.html +#[cfg(any(feature = "derive", test))] +#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))] +pub use zerocopy_derive::AsBytes; + +/// Types that can be viewed as an immutable slice of initialized bytes. +/// +/// Any `AsBytes` type can be viewed as a slice of initialized bytes of the same +/// size. This is useful for efficiently serializing structured data as raw +/// bytes. +/// +/// # Implementation +/// +/// **Do not implement this trait yourself!** Instead, use +/// [`#[derive(AsBytes)]`][derive] (requires the `derive` Cargo feature); e.g.: +/// +/// ``` +/// # use zerocopy_derive::AsBytes; +/// #[derive(AsBytes)] +/// #[repr(C)] +/// struct MyStruct { +/// # /* +/// ... +/// # */ +/// } +/// +/// #[derive(AsBytes)] +/// #[repr(u8)] +/// enum MyEnum { +/// # Variant0, +/// # /* +/// ... +/// # */ +/// } +/// +/// #[derive(AsBytes)] +/// #[repr(C)] +/// union MyUnion { +/// # variant: u8, +/// # /* +/// ... +/// # */ +/// } +/// ``` +/// +/// This derive performs a sophisticated, compile-time safety analysis to +/// determine whether a type is `AsBytes`. See the [derive +/// documentation][derive] for guidance on how to interpret error messages +/// produced by the derive's analysis. +/// +/// # Safety +/// +/// *This section describes what is required in order for `T: AsBytes`, and +/// what unsafe code may assume of such types. If you don't plan on implementing +/// `AsBytes` manually, and you don't plan on writing unsafe code that +/// operates on `AsBytes` types, then you don't need to read this section.* +/// +/// If `T: AsBytes`, then unsafe code may assume that: +/// - It is sound to treat any `t: T` as an immutable `[u8]` of length +/// `size_of_val(t)`. +/// - Given `t: &T`, it is sound to construct a `b: &[u8]` where `b.len() == +/// size_of_val(t)` at the same address as `t`, and it is sound for both `b` +/// and `t` to be live at the same time. +/// +/// If a type is marked as `AsBytes` which violates this contract, it may cause +/// undefined behavior. +/// +/// `#[derive(AsBytes)]` only permits [types which satisfy these +/// requirements][derive-analysis]. +/// +#[cfg_attr( + feature = "derive", + doc = "[derive]: zerocopy_derive::AsBytes", + doc = "[derive-analysis]: zerocopy_derive::AsBytes#analysis" +)] +#[cfg_attr( + not(feature = "derive"), + doc = concat!("[derive]: https://docs.rs/zerocopy/", "0.7.29", "/zerocopy/derive.AsBytes.html"), + doc = concat!("[derive-analysis]: https://docs.rs/zerocopy/", "0.7.29", "/zerocopy/derive.AsBytes.html#analysis"), +)] pub unsafe trait AsBytes { // The `Self: Sized` bound makes it so that this function doesn't prevent // `AsBytes` from being object safe. Note that other `AsBytes` methods @@ -1003,6 +2661,33 @@ pub unsafe trait AsBytes { /// /// `as_bytes` provides access to the bytes of this value as an immutable /// byte slice. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::AsBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(AsBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// let header = PacketHeader { + /// src_port: [0, 1], + /// dst_port: [2, 3], + /// length: [4, 5], + /// checksum: [6, 7], + /// }; + /// + /// let bytes = header.as_bytes(); + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]); + /// ``` #[inline(always)] fn as_bytes(&self) -> &[u8] { // Note that this method does not have a `Self: Sized` bound; @@ -1029,6 +2714,8 @@ pub unsafe trait AsBytes { // - The total size of the resulting slice is no larger than // `isize::MAX` because no allocation produced by safe code can be // larger than `isize::MAX`. + // + // TODO(#429): Add references to docs and quotes. unsafe { slice::from_raw_parts(slf.cast::<u8>(), len) } } @@ -1036,6 +2723,43 @@ pub unsafe trait AsBytes { /// /// `as_bytes_mut` provides access to the bytes of this value as a mutable /// byte slice. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::AsBytes; + /// # use zerocopy_derive::*; + /// + /// # #[derive(Eq, PartialEq, Debug)] + /// #[derive(AsBytes, FromZeroes, FromBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// let mut header = PacketHeader { + /// src_port: [0, 1], + /// dst_port: [2, 3], + /// length: [4, 5], + /// checksum: [6, 7], + /// }; + /// + /// let bytes = header.as_bytes_mut(); + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]); + /// + /// bytes.reverse(); + /// + /// assert_eq!(header, PacketHeader { + /// src_port: [7, 6], + /// dst_port: [5, 4], + /// length: [3, 2], + /// checksum: [1, 0], + /// }); + /// ``` #[inline(always)] fn as_bytes_mut(&mut self) -> &mut [u8] where @@ -1064,12 +2788,57 @@ pub unsafe trait AsBytes { // - The total size of the resulting slice is no larger than // `isize::MAX` because no allocation produced by safe code can be // larger than `isize::MAX`. + // + // TODO(#429): Add references to docs and quotes. unsafe { slice::from_raw_parts_mut(slf.cast::<u8>(), len) } } /// Writes a copy of `self` to `bytes`. /// /// If `bytes.len() != size_of_val(self)`, `write_to` returns `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::AsBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(AsBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// let header = PacketHeader { + /// src_port: [0, 1], + /// dst_port: [2, 3], + /// length: [4, 5], + /// checksum: [6, 7], + /// }; + /// + /// let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0]; + /// + /// header.write_to(&mut bytes[..]); + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]); + /// ``` + /// + /// If too many or too few target bytes are provided, `write_to` returns + /// `None` and leaves the target bytes unmodified: + /// + /// ``` + /// # use zerocopy::AsBytes; + /// # let header = u128::MAX; + /// let mut excessive_bytes = &mut [0u8; 128][..]; + /// + /// let write_result = header.write_to(excessive_bytes); + /// + /// assert!(write_result.is_none()); + /// assert_eq!(excessive_bytes, [0u8; 128]); + /// ``` #[inline] fn write_to(&self, bytes: &mut [u8]) -> Option<()> { if bytes.len() != mem::size_of_val(self) { @@ -1084,6 +2853,49 @@ pub unsafe trait AsBytes { /// /// `write_to_prefix` writes `self` to the first `size_of_val(self)` bytes /// of `bytes`. If `bytes.len() < size_of_val(self)`, it returns `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::AsBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(AsBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// let header = PacketHeader { + /// src_port: [0, 1], + /// dst_port: [2, 3], + /// length: [4, 5], + /// checksum: [6, 7], + /// }; + /// + /// let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + /// + /// header.write_to_prefix(&mut bytes[..]); + /// + /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7, 0, 0]); + /// ``` + /// + /// If insufficient target bytes are provided, `write_to_prefix` returns + /// `None` and leaves the target bytes unmodified: + /// + /// ``` + /// # use zerocopy::AsBytes; + /// # let header = u128::MAX; + /// let mut insufficent_bytes = &mut [0, 0][..]; + /// + /// let write_result = header.write_to_suffix(insufficent_bytes); + /// + /// assert!(write_result.is_none()); + /// assert_eq!(insufficent_bytes, [0, 0]); + /// ``` #[inline] fn write_to_prefix(&self, bytes: &mut [u8]) -> Option<()> { let size = mem::size_of_val(self); @@ -1095,6 +2907,56 @@ pub unsafe trait AsBytes { /// /// `write_to_suffix` writes `self` to the last `size_of_val(self)` bytes of /// `bytes`. If `bytes.len() < size_of_val(self)`, it returns `None`. + /// + /// # Examples + /// + /// ``` + /// use zerocopy::AsBytes; + /// # use zerocopy_derive::*; + /// + /// #[derive(AsBytes)] + /// #[repr(C)] + /// struct PacketHeader { + /// src_port: [u8; 2], + /// dst_port: [u8; 2], + /// length: [u8; 2], + /// checksum: [u8; 2], + /// } + /// + /// let header = PacketHeader { + /// src_port: [0, 1], + /// dst_port: [2, 3], + /// length: [4, 5], + /// checksum: [6, 7], + /// }; + /// + /// let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + /// + /// header.write_to_suffix(&mut bytes[..]); + /// + /// assert_eq!(bytes, [0, 0, 0, 1, 2, 3, 4, 5, 6, 7]); + /// + /// let mut insufficent_bytes = &mut [0, 0][..]; + /// + /// let write_result = header.write_to_suffix(insufficent_bytes); + /// + /// assert!(write_result.is_none()); + /// assert_eq!(insufficent_bytes, [0, 0]); + /// ``` + /// + /// If insufficient target bytes are provided, `write_to_suffix` returns + /// `None` and leaves the target bytes unmodified: + /// + /// ``` + /// # use zerocopy::AsBytes; + /// # let header = u128::MAX; + /// let mut insufficent_bytes = &mut [0, 0][..]; + /// + /// let write_result = header.write_to_suffix(insufficent_bytes); + /// + /// assert!(write_result.is_none()); + /// assert_eq!(insufficent_bytes, [0, 0]); + /// ``` #[inline] fn write_to_suffix(&self, bytes: &mut [u8]) -> Option<()> { let start = bytes.len().checked_sub(mem::size_of_val(self))?; @@ -1139,101 +3001,206 @@ safety_comment! { /// SAFETY: /// Per the reference [1], "the unit tuple (`()`) ... is guaranteed as a /// zero-sized type to have a size of 0 and an alignment of 1." - /// - `FromZeroes`, `FromBytes`: There is only one possible sequence of 0 - /// bytes, and `()` is inhabited. + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: There + /// is only one possible sequence of 0 bytes, and `()` is inhabited. /// - `AsBytes`: Since `()` has size 0, it contains no padding bytes. /// - `Unaligned`: `()` has alignment 1. /// /// [1] https://doc.rust-lang.org/reference/type-layout.html#tuple-layout - unsafe_impl!((): FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!((): TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); assert_unaligned!(()); } safety_comment! { /// SAFETY: - /// - `FromZeroes`, `FromBytes`: all bit patterns are valid for integers [1] - /// - `AsBytes`: integers have no padding bytes [1] + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: all bit + /// patterns are valid for numeric types [1] + /// - `AsBytes`: numeric types have no padding bytes [1] /// - `Unaligned` (`u8` and `i8` only): The reference [2] specifies the size /// of `u8` and `i8` as 1 byte. We also know that: - /// - Alignment is >= 1 - /// - Size is an integer multiple of alignment + /// - Alignment is >= 1 [3] + /// - Size is an integer multiple of alignment [4] /// - The only value >= 1 for which 1 is an integer multiple is 1 /// Therefore, the only possible alignment for `u8` and `i8` is 1. /// - /// [1] TODO(https://github.com/rust-lang/reference/issues/1291): Once the - /// reference explicitly guarantees these properties, cite it. + /// [1] Per https://doc.rust-lang.org/beta/reference/types/numeric.html#bit-validity: + /// + /// For every numeric type, `T`, the bit validity of `T` is equivalent to + /// the bit validity of `[u8; size_of::<T>()]`. An uninitialized byte is + /// not a valid `u8`. + /// + /// TODO(https://github.com/rust-lang/reference/pull/1392): Once this text + /// is available on the Stable docs, cite those instead. + /// /// [2] https://doc.rust-lang.org/reference/type-layout.html#primitive-data-layout - unsafe_impl!(u8: FromZeroes, FromBytes, AsBytes, Unaligned); - unsafe_impl!(i8: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_unaligned!(u8, i8); - unsafe_impl!(u16: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i16: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(u32: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i32: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(u64: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i64: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(u128: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i128: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(usize: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(isize: FromZeroes, FromBytes, AsBytes); -} - -safety_comment! { - /// SAFETY: - /// - `FromZeroes`, `FromBytes`: the `{f32,f64}::from_bits` constructors' - /// documentation [1,2] states that they are currently equivalent to - /// `transmute`. [3] - /// - `AsBytes`: the `{f32,f64}::to_bits` methods' documentation [4,5] - /// states that they are currently equivalent to `transmute`. [3] /// - /// TODO: Make these arguments more precisely in terms of the documentation. + /// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: + /// + /// Alignment is measured in bytes, and must be at least 1. + /// + /// [4] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: /// - /// [1] https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.from_bits - /// [2] https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.from_bits - /// [3] TODO(https://github.com/rust-lang/reference/issues/1291): Once the - /// reference explicitly guarantees these properties, cite it. - /// [4] https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.to_bits - /// [5] https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.to_bits - unsafe_impl!(f32: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(f64: FromZeroes, FromBytes, AsBytes); + /// The size of a value is always a multiple of its alignment. + /// + /// TODO(#278): Once we've updated the trait docs to refer to `u8`s rather + /// than bits or bytes, update this comment, especially the reference to + /// [1]. + unsafe_impl!(u8: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!(i8: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_unaligned!(u8, i8); + unsafe_impl!(u16: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i16: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(u32: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i32: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(u64: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i64: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(u128: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i128: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(usize: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(isize: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(f32: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(f64: TryFromBytes, FromZeroes, FromBytes, AsBytes); } safety_comment! { /// SAFETY: - /// - `FromZeroes`: Per the reference [1], 0x00 is a valid bit pattern for - /// `bool`. - /// - `AsBytes`: Per the reference [1], `bool` always has a size of 1 with - /// valid bit patterns 0x01 and 0x00, so the only byte of the bool is - /// always initialized + /// - `FromZeroes`: Valid since "[t]he value false has the bit pattern + /// 0x00" [1]. + /// - `AsBytes`: Since "the boolean type has a size and alignment of 1 each" + /// and "The value false has the bit pattern 0x00 and the value true has + /// the bit pattern 0x01" [1]. Thus, the only byte of the bool is always + /// initialized. /// - `Unaligned`: Per the reference [1], "[a]n object with the boolean type /// has a size and alignment of 1 each." /// /// [1] https://doc.rust-lang.org/reference/types/boolean.html unsafe_impl!(bool: FromZeroes, AsBytes, Unaligned); assert_unaligned!(bool); + /// SAFETY: + /// - The safety requirements for `unsafe_impl!` with an `is_bit_valid` + /// closure: + /// - Given `t: *mut bool` and `let r = *mut u8`, `r` refers to an object + /// of the same size as that referred to by `t`. This is true because + /// `bool` and `u8` have the same size (1 byte) [1]. + /// - Since the closure takes a `&u8` argument, given a `Ptr<'a, bool>` + /// which satisfies the preconditions of + /// `TryFromBytes::<bool>::is_bit_valid`, it must be guaranteed that the + /// memory referenced by that `Ptr` always contains a valid `u8`. Since + /// `bool`'s single byte is always initialized, `is_bit_valid`'s + /// precondition requires that the same is true of its argument. Since + /// `u8`'s only bit validity invariant is that its single byte must be + /// initialized, this memory is guaranteed to contain a valid `u8`. + /// - The alignment of `bool` is equal to the alignment of `u8`. [1] [2] + /// - The impl must only return `true` for its argument if the original + /// `Ptr<bool>` refers to a valid `bool`. We only return true if the + /// `u8` value is 0 or 1, and both of these are valid values for `bool`. + /// [3] + /// + /// [1] Per https://doc.rust-lang.org/reference/type-layout.html#primitive-data-layout: + /// + /// The size of most primitives is given in this table. + /// + /// | Type | `size_of::<Type>() ` | + /// |-----------|----------------------| + /// | `bool` | 1 | + /// | `u8`/`i8` | 1 | + /// + /// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: + /// + /// The size of a value is always a multiple of its alignment. + /// + /// [3] Per https://doc.rust-lang.org/reference/types/boolean.html: + /// + /// The value false has the bit pattern 0x00 and the value true has the + /// bit pattern 0x01. + unsafe_impl!(bool: TryFromBytes; |byte: &u8| *byte < 2); } safety_comment! { /// SAFETY: - /// - `FromZeroes`: Per the reference [1], 0x0000 is a valid bit pattern for - /// `char`. - /// - `AsBytes`: `char` is represented as a 32-bit unsigned word (`u32`) - /// [1], which is `AsBytes`. Note that unlike `u32`, not all bit patterns - /// are valid for `char`. + /// - `FromZeroes`: Per reference [1], "[a] value of type char is a Unicode + /// scalar value (i.e. a code point that is not a surrogate), represented + /// as a 32-bit unsigned word in the 0x0000 to 0xD7FF or 0xE000 to + /// 0x10FFFF range" which contains 0x0000. + /// - `AsBytes`: `char` is per reference [1] "represented as a 32-bit + /// unsigned word" (`u32`) which is `AsBytes`. Note that unlike `u32`, not + /// all bit patterns are valid for `char`. /// /// [1] https://doc.rust-lang.org/reference/types/textual.html unsafe_impl!(char: FromZeroes, AsBytes); + /// SAFETY: + /// - The safety requirements for `unsafe_impl!` with an `is_bit_valid` + /// closure: + /// - Given `t: *mut char` and `let r = *mut u32`, `r` refers to an object + /// of the same size as that referred to by `t`. This is true because + /// `char` and `u32` have the same size [1]. + /// - Since the closure takes a `&u32` argument, given a `Ptr<'a, char>` + /// which satisfies the preconditions of + /// `TryFromBytes::<char>::is_bit_valid`, it must be guaranteed that the + /// memory referenced by that `Ptr` always contains a valid `u32`. Since + /// `char`'s bytes are always initialized [2], `is_bit_valid`'s + /// precondition requires that the same is true of its argument. Since + /// `u32`'s only bit validity invariant is that its bytes must be + /// initialized, this memory is guaranteed to contain a valid `u32`. + /// - The alignment of `char` is equal to the alignment of `u32`. [1] + /// - The impl must only return `true` for its argument if the original + /// `Ptr<char>` refers to a valid `char`. `char::from_u32` guarantees + /// that it returns `None` if its input is not a valid `char`. [3] + /// + /// [1] Per https://doc.rust-lang.org/nightly/reference/types/textual.html#layout-and-bit-validity: + /// + /// `char` is guaranteed to have the same size and alignment as `u32` on + /// all platforms. + /// + /// [2] Per https://doc.rust-lang.org/core/primitive.char.html#method.from_u32: + /// + /// Every byte of a `char` is guaranteed to be initialized. + /// + /// [3] Per https://doc.rust-lang.org/core/primitive.char.html#method.from_u32: + /// + /// `from_u32()` will return `None` if the input is not a valid value for + /// a `char`. + unsafe_impl!(char: TryFromBytes; |candidate: &u32| char::from_u32(*candidate).is_some()); } safety_comment! { /// SAFETY: - /// - `FromZeroes`, `AsBytes`, `Unaligned`: Per the reference [1], `str` has - /// the same layout as `[u8]`, and `[u8]` is `FromZeroes`, `AsBytes`, and - /// `Unaligned`. + /// - `FromZeroes`, `AsBytes`, `Unaligned`: Per the reference [1], `str` + /// has the same layout as `[u8]`, and `[u8]` is `FromZeroes`, `AsBytes`, + /// and `Unaligned`. /// /// Note that we don't `assert_unaligned!(str)` because `assert_unaligned!` /// uses `align_of`, which only works for `Sized` types. /// + /// TODO(#429): Add quotes from documentation. + /// /// [1] https://doc.rust-lang.org/reference/type-layout.html#str-layout unsafe_impl!(str: FromZeroes, AsBytes, Unaligned); + /// SAFETY: + /// - The safety requirements for `unsafe_impl!` with an `is_bit_valid` + /// closure: + /// - Given `t: *mut str` and `let r = *mut [u8]`, `r` refers to an object + /// of the same size as that referred to by `t`. This is true because + /// `str` and `[u8]` have the same representation. [1] + /// - Since the closure takes a `&[u8]` argument, given a `Ptr<'a, str>` + /// which satisfies the preconditions of + /// `TryFromBytes::<str>::is_bit_valid`, it must be guaranteed that the + /// memory referenced by that `Ptr` always contains a valid `[u8]`. + /// Since `str`'s bytes are always initialized [1], `is_bit_valid`'s + /// precondition requires that the same is true of its argument. Since + /// `[u8]`'s only bit validity invariant is that its bytes must be + /// initialized, this memory is guaranteed to contain a valid `[u8]`. + /// - The alignment of `str` is equal to the alignment of `[u8]`. [1] + /// - The impl must only return `true` for its argument if the original + /// `Ptr<str>` refers to a valid `str`. `str::from_utf8` guarantees that + /// it returns `Err` if its input is not a valid `str`. [2] + /// + /// [1] Per https://doc.rust-lang.org/reference/types/textual.html: + /// + /// A value of type `str` is represented the same was as `[u8]`. + /// + /// [2] Per https://doc.rust-lang.org/core/str/fn.from_utf8.html#errors: + /// + /// Returns `Err` if the slice is not UTF-8. + unsafe_impl!(str: TryFromBytes; |candidate: &[u8]| core::str::from_utf8(candidate).is_ok()); } safety_comment! { @@ -1254,6 +3221,8 @@ safety_comment! { /// be 0 bytes, which means that they must be 1 byte. The only valid /// alignment for a 1-byte type is 1. /// + /// TODO(#429): Add quotes from documentation. + /// /// [1] https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html /// [2] https://doc.rust-lang.org/stable/std/num/struct.NonZeroI8.html /// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation @@ -1271,12 +3240,50 @@ safety_comment! { unsafe_impl!(NonZeroI128: AsBytes); unsafe_impl!(NonZeroUsize: AsBytes); unsafe_impl!(NonZeroIsize: AsBytes); + /// SAFETY: + /// - The safety requirements for `unsafe_impl!` with an `is_bit_valid` + /// closure: + /// - Given `t: *mut NonZeroXxx` and `let r = *mut xxx`, `r` refers to an + /// object of the same size as that referred to by `t`. This is true + /// because `NonZeroXxx` and `xxx` have the same size. [1] + /// - Since the closure takes a `&xxx` argument, given a `Ptr<'a, + /// NonZeroXxx>` which satisfies the preconditions of + /// `TryFromBytes::<NonZeroXxx>::is_bit_valid`, it must be guaranteed + /// that the memory referenced by that `Ptr` always contains a valid + /// `xxx`. Since `NonZeroXxx`'s bytes are always initialized [1], + /// `is_bit_valid`'s precondition requires that the same is true of its + /// argument. Since `xxx`'s only bit validity invariant is that its + /// bytes must be initialized, this memory is guaranteed to contain a + /// valid `xxx`. + /// - The alignment of `NonZeroXxx` is equal to the alignment of `xxx`. + /// [1] + /// - The impl must only return `true` for its argument if the original + /// `Ptr<NonZeroXxx>` refers to a valid `NonZeroXxx`. The only `xxx` + /// which is not also a valid `NonZeroXxx` is 0. [1] + /// + /// [1] Per https://doc.rust-lang.org/core/num/struct.NonZeroU16.html: + /// + /// `NonZeroU16` is guaranteed to have the same layout and bit validity as + /// `u16` with the exception that `0` is not a valid instance. + unsafe_impl!(NonZeroU8: TryFromBytes; |n: &u8| *n != 0); + unsafe_impl!(NonZeroI8: TryFromBytes; |n: &i8| *n != 0); + unsafe_impl!(NonZeroU16: TryFromBytes; |n: &u16| *n != 0); + unsafe_impl!(NonZeroI16: TryFromBytes; |n: &i16| *n != 0); + unsafe_impl!(NonZeroU32: TryFromBytes; |n: &u32| *n != 0); + unsafe_impl!(NonZeroI32: TryFromBytes; |n: &i32| *n != 0); + unsafe_impl!(NonZeroU64: TryFromBytes; |n: &u64| *n != 0); + unsafe_impl!(NonZeroI64: TryFromBytes; |n: &i64| *n != 0); + unsafe_impl!(NonZeroU128: TryFromBytes; |n: &u128| *n != 0); + unsafe_impl!(NonZeroI128: TryFromBytes; |n: &i128| *n != 0); + unsafe_impl!(NonZeroUsize: TryFromBytes; |n: &usize| *n != 0); + unsafe_impl!(NonZeroIsize: TryFromBytes; |n: &isize| *n != 0); } safety_comment! { /// SAFETY: - /// - `FromZeroes`, `FromBytes`, `AsBytes`: The Rust compiler reuses `0` - /// value to represent `None`, so `size_of::<Option<NonZeroXxx>>() == - /// size_of::<xxx>()`; see `NonZeroXxx` documentation. + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`, + /// `AsBytes`: The Rust compiler reuses `0` value to represent `None`, so + /// `size_of::<Option<NonZeroXxx>>() == size_of::<xxx>()`; see + /// `NonZeroXxx` documentation. /// - `Unaligned`: `NonZeroU8` and `NonZeroI8` document that /// `Option<NonZeroU8>` and `Option<NonZeroI8>` both have size 1. [1] [2] /// This is worded in a way that makes it unclear whether it's meant as a @@ -1284,37 +3291,82 @@ safety_comment! { /// unthinkable that that would ever change. The only valid alignment for /// a 1-byte type is 1. /// + /// TODO(#429): Add quotes from documentation. + /// /// [1] https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html /// [2] https://doc.rust-lang.org/stable/std/num/struct.NonZeroI8.html /// /// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation /// for layout guarantees. - unsafe_impl!(Option<NonZeroU8>: FromZeroes, FromBytes, AsBytes, Unaligned); - unsafe_impl!(Option<NonZeroI8>: FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!(Option<NonZeroU8>: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!(Option<NonZeroI8>: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); assert_unaligned!(Option<NonZeroU8>, Option<NonZeroI8>); - unsafe_impl!(Option<NonZeroU16>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroI16>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroU32>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroI32>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroU64>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroI64>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroU128>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroI128>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroUsize>: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option<NonZeroIsize>: FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroU16>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroI16>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroU32>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroI32>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroU64>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroI64>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroU128>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroI128>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroUsize>: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option<NonZeroIsize>: TryFromBytes, FromZeroes, FromBytes, AsBytes); +} + +safety_comment! { + /// SAFETY: + /// The following types can be transmuted from `[0u8; size_of::<T>()]`. [1] + /// None of them contain `UnsafeCell`s, and so they all soundly implement + /// `FromZeroes`. + /// + /// [1] Per + /// https://doc.rust-lang.org/nightly/core/option/index.html#representation: + /// + /// Rust guarantees to optimize the following types `T` such that + /// [`Option<T>`] has the same size and alignment as `T`. In some of these + /// cases, Rust further guarantees that `transmute::<_, Option<T>>([0u8; + /// size_of::<T>()])` is sound and produces `Option::<T>::None`. These + /// cases are identified by the second column: + /// + /// | `T` | `transmute::<_, Option<T>>([0u8; size_of::<T>()])` sound? | + /// |-----------------------|-----------------------------------------------------------| + /// | [`Box<U>`] | when `U: Sized` | + /// | `&U` | when `U: Sized` | + /// | `&mut U` | when `U: Sized` | + /// | [`ptr::NonNull<U>`] | when `U: Sized` | + /// | `fn`, `extern "C" fn` | always | + /// + /// TODO(#429), TODO(https://github.com/rust-lang/rust/pull/115333): Cite + /// the Stable docs once they're available. + #[cfg(feature = "alloc")] + unsafe_impl!( + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + T => FromZeroes for Option<Box<T>> + ); + unsafe_impl!(T => FromZeroes for Option<&'_ T>); + unsafe_impl!(T => FromZeroes for Option<&'_ mut T>); + unsafe_impl!(T => FromZeroes for Option<NonNull<T>>); + unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeroes for opt_fn!(...)); + unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeroes for opt_extern_c_fn!(...)); } safety_comment! { /// SAFETY: - /// For all `T`, `PhantomData<T>` has size 0 and alignment 1. [1] - /// - `FromZeroes`, `FromBytes`: There is only one possible sequence of 0 - /// bytes, and `PhantomData` is inhabited. + /// Per reference [1]: + /// "For all T, the following are guaranteed: + /// size_of::<PhantomData<T>>() == 0 + /// align_of::<PhantomData<T>>() == 1". + /// This gives: + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: There + /// is only one possible sequence of 0 bytes, and `PhantomData` is + /// inhabited. /// - `AsBytes`: Since `PhantomData` has size 0, it contains no padding /// bytes. /// - `Unaligned`: Per the preceding reference, `PhantomData` has alignment /// 1. /// /// [1] https://doc.rust-lang.org/std/marker/struct.PhantomData.html#layout-1 + unsafe_impl!(T: ?Sized => TryFromBytes for PhantomData<T>); unsafe_impl!(T: ?Sized => FromZeroes for PhantomData<T>); unsafe_impl!(T: ?Sized => FromBytes for PhantomData<T>); unsafe_impl!(T: ?Sized => AsBytes for PhantomData<T>); @@ -1323,13 +3375,59 @@ safety_comment! { } safety_comment! { /// SAFETY: - /// `Wrapping<T>` is guaranteed by its docs [1] to have the same layout as - /// `T`. Also, `Wrapping<T>` is `#[repr(transparent)]`, and has a single - /// field, which is `pub`. Per the reference [2], this means that the - /// `#[repr(transparent)]` attribute is "considered part of the public ABI". + /// `Wrapping<T>` is guaranteed by its docs [1] to have the same layout and + /// bit validity as `T`. Also, `Wrapping<T>` is `#[repr(transparent)]`, and + /// has a single field, which is `pub`. Per the reference [2], this means + /// that the `#[repr(transparent)]` attribute is "considered part of the + /// public ABI". + /// + /// - `TryFromBytes`: The safety requirements for `unsafe_impl!` with an + /// `is_bit_valid` closure: + /// - Given `t: *mut Wrapping<T>` and `let r = *mut T`, `r` refers to an + /// object of the same size as that referred to by `t`. This is true + /// because `Wrapping<T>` and `T` have the same layout + /// - The alignment of `Wrapping<T>` is equal to the alignment of `T`. + /// - The impl must only return `true` for its argument if the original + /// `Ptr<Wrapping<T>>` refers to a valid `Wrapping<T>`. Since + /// `Wrapping<T>` has the same bit validity as `T`, and since our impl + /// just calls `T::is_bit_valid`, our impl returns `true` exactly when + /// its argument contains a valid `Wrapping<T>`. + /// - `FromBytes`: Since `Wrapping<T>` has the same bit validity as `T`, if + /// `T: FromBytes`, then all initialized byte sequences are valid + /// instances of `Wrapping<T>`. Similarly, if `T: FromBytes`, then + /// `Wrapping<T>` doesn't contain any `UnsafeCell`s. Thus, `impl FromBytes + /// for Wrapping<T> where T: FromBytes` is a sound impl. + /// - `AsBytes`: Since `Wrapping<T>` has the same bit validity as `T`, if + /// `T: AsBytes`, then all valid instances of `Wrapping<T>` have all of + /// their bytes initialized. Similarly, if `T: AsBytes`, then + /// `Wrapping<T>` doesn't contain any `UnsafeCell`s. Thus, `impl AsBytes + /// for Wrapping<T> where T: AsBytes` is a valid impl. + /// - `Unaligned`: Since `Wrapping<T>` has the same layout as `T`, + /// `Wrapping<T>` has alignment 1 exactly when `T` does. + /// + /// [1] Per https://doc.rust-lang.org/core/num/struct.NonZeroU16.html: + /// + /// `NonZeroU16` is guaranteed to have the same layout and bit validity as + /// `u16` with the exception that `0` is not a valid instance. + /// + /// TODO(#429): Add quotes from documentation. + /// + /// [1] TODO(https://doc.rust-lang.org/nightly/core/num/struct.Wrapping.html#layout-1): + /// Reference this documentation once it's available on stable. /// - /// [1] https://doc.rust-lang.org/nightly/core/num/struct.Wrapping.html#layout-1 /// [2] https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent + unsafe_impl!(T: TryFromBytes => TryFromBytes for Wrapping<T>; |candidate: Ptr<T>| { + // SAFETY: + // - Since `T` and `Wrapping<T>` have the same layout and bit validity + // and contain the same fields, `T` contains `UnsafeCell`s exactly + // where `Wrapping<T>` does. Thus, all memory and `UnsafeCell` + // preconditions of `T::is_bit_valid` hold exactly when the same + // preconditions for `Wrapping<T>::is_bit_valid` hold. + // - By the same token, since `candidate` is guaranteed to have its + // bytes initialized where there are always initialized bytes in + // `Wrapping<T>`, the same is true for `T`. + unsafe { T::is_bit_valid(candidate) } + }); unsafe_impl!(T: FromZeroes => FromZeroes for Wrapping<T>); unsafe_impl!(T: FromBytes => FromBytes for Wrapping<T>); unsafe_impl!(T: AsBytes => AsBytes for Wrapping<T>); @@ -1341,22 +3439,23 @@ safety_comment! { // since it may contain uninitialized bytes. // /// SAFETY: - /// - `FromZeroes`, `FromBytes`: `MaybeUninit<T>` has no restrictions on its - /// contents. Unfortunately, in addition to bit validity, `FromZeroes` and + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: + /// `MaybeUninit<T>` has no restrictions on its contents. Unfortunately, + /// in addition to bit validity, `TryFromBytes`, `FromZeroes` and /// `FromBytes` also require that implementers contain no `UnsafeCell`s. - /// Thus, we require `T: FromZeroes` and `T: FromBytes` in order to ensure - /// that `T` - and thus `MaybeUninit<T>` - contains to `UnsafeCell`s. - /// Thus, requiring that `T` implement each of these traits is sufficient - /// - `Unaligned`: `MaybeUninit<T>` is guaranteed by its documentation [1] - /// to have the same alignment as `T`. + /// Thus, we require `T: Trait` in order to ensure that `T` - and thus + /// `MaybeUninit<T>` - contains to `UnsafeCell`s. Thus, requiring that `T` + /// implement each of these traits is sufficient. + /// - `Unaligned`: "MaybeUninit<T> is guaranteed to have the same size, + /// alignment, and ABI as T" [1] /// - /// [1] - /// https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1 + /// [1] https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#layout-1 /// /// TODO(https://github.com/google/zerocopy/issues/251): If we split /// `FromBytes` and `RefFromBytes`, or if we introduce a separate /// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes` /// and `FromBytes`. + unsafe_impl!(T: TryFromBytes => TryFromBytes for MaybeUninit<T>); unsafe_impl!(T: FromZeroes => FromZeroes for MaybeUninit<T>); unsafe_impl!(T: FromBytes => FromBytes for MaybeUninit<T>); unsafe_impl!(T: Unaligned => Unaligned for MaybeUninit<T>); @@ -1364,9 +3463,10 @@ safety_comment! { } safety_comment! { /// SAFETY: - /// `ManuallyDrop` has the same layout as `T`, and accessing the inner value - /// is safe (meaning that it's unsound to leave the inner value - /// uninitialized while exposing the `ManuallyDrop` to safe code). + /// `ManuallyDrop` has the same layout and bit validity as `T` [1], and + /// accessing the inner value is safe (meaning that it's unsound to leave + /// the inner value uninitialized while exposing the `ManuallyDrop` to safe + /// code). /// - `FromZeroes`, `FromBytes`: Since it has the same layout as `T`, any /// valid `T` is a valid `ManuallyDrop<T>`. If `T: FromZeroes`, a sequence /// of zero bytes is a valid `T`, and thus a valid `ManuallyDrop<T>`. If @@ -1379,6 +3479,17 @@ safety_comment! { /// code can only ever access a `ManuallyDrop` with all initialized bytes. /// - `Unaligned`: `ManuallyDrop` has the same layout (and thus alignment) /// as `T`, and `T: Unaligned` guarantees that that alignment is 1. + /// + /// `ManuallyDrop<T>` is guaranteed to have the same layout and bit + /// validity as `T` + /// + /// [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html: + /// + /// TODO(#429): + /// - Add quotes from docs. + /// - Once [1] (added in + /// https://github.com/rust-lang/rust/pull/115522) is available on stable, + /// quote the stable docs instead of the nightly docs. unsafe_impl!(T: ?Sized + FromZeroes => FromZeroes for ManuallyDrop<T>); unsafe_impl!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop<T>); unsafe_impl!(T: ?Sized + AsBytes => AsBytes for ManuallyDrop<T>); @@ -1400,8 +3511,8 @@ safety_comment! { /// /// In other words, the layout of a `[T]` or `[T; N]` is a sequence of `T`s /// laid out back-to-back with no bytes in between. Therefore, `[T]` or `[T; - /// N]` are `FromZeroes`, `FromBytes`, and `AsBytes` if `T` is - /// (respectively). Furthermore, since an array/slice has "the same + /// N]` are `TryFromBytes`, `FromZeroes`, `FromBytes`, and `AsBytes` if `T` + /// is (respectively). Furthermore, since an array/slice has "the same /// alignment of `T`", `[T]` and `[T; N]` are `Unaligned` if `T` is. /// /// Note that we don't `assert_unaligned!` for slice types because @@ -1413,11 +3524,63 @@ safety_comment! { unsafe_impl!(const N: usize, T: AsBytes => AsBytes for [T; N]); unsafe_impl!(const N: usize, T: Unaligned => Unaligned for [T; N]); assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]); + unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c: Ptr<[T]>| { + // SAFETY: Assuming the preconditions of `is_bit_valid` are satisfied, + // so too will the postcondition: that, if `is_bit_valid(candidate)` + // returns true, `*candidate` contains a valid `Self`. Per the reference + // [1]: + // + // An array of `[T; N]` has a size of `size_of::<T>() * N` and the + // same alignment of `T`. Arrays are laid out so that the zero-based + // `nth` element of the array is offset from the start of the array by + // `n * size_of::<T>()` bytes. + // + // ... + // + // Slices have the same layout as the section of the array they slice. + // + // In other words, the layout of a `[T] is a sequence of `T`s laid out + // back-to-back with no bytes in between. If all elements in `candidate` + // are `is_bit_valid`, so too is `candidate`. + // + // Note that any of the below calls may panic, but it would still be + // sound even if it did. `is_bit_valid` does not promise that it will + // not panic (in fact, it explicitly warns that it's a possibility), and + // we have not violated any safety invariants that we must fix before + // returning. + c.iter().all(|elem| + // SAFETY: We uphold the safety contract of `is_bit_valid(elem)`, by + // precondition on the surrounding call to `is_bit_valid`. The + // memory referenced by `elem` is contained entirely within `c`, and + // satisfies the preconditions satisfied by `c`. By axiom, we assume + // that `Iterator:all` does not invalidate these preconditions + // (e.g., by writing to `elem`.) Since `elem` is derived from `c`, + // it is only possible for uninitialized bytes to occur in `elem` at + // the same bytes they occur within `c`. + unsafe { <T as TryFromBytes>::is_bit_valid(elem) } + ) + }); unsafe_impl!(T: FromZeroes => FromZeroes for [T]); unsafe_impl!(T: FromBytes => FromBytes for [T]); unsafe_impl!(T: AsBytes => AsBytes for [T]); unsafe_impl!(T: Unaligned => Unaligned for [T]); } +safety_comment! { + /// SAFETY: + /// - `FromZeroes`: For thin pointers (note that `T: Sized`), the zero + /// pointer is considered "null". [1] No operations which require + /// provenance are legal on null pointers, so this is not a footgun. + /// + /// NOTE(#170): Implementing `FromBytes` and `AsBytes` for raw pointers + /// would be sound, but carries provenance footguns. We want to support + /// `FromBytes` and `AsBytes` for raw pointers eventually, but we are + /// holding off until we can figure out how to address those footguns. + /// + /// [1] TODO(https://github.com/rust-lang/rust/pull/116988): Cite the + /// documentation once this PR lands. + unsafe_impl!(T => FromZeroes for *const T); + unsafe_impl!(T => FromZeroes for *mut T); +} // SIMD support // @@ -1464,8 +3627,8 @@ safety_comment! { // Given this background, we can observe that: // - The size and bit pattern requirements of a SIMD type are equivalent to the // equivalent array type. Thus, for any SIMD type whose primitive `T` is -// `FromZeroes`, `FromBytes`, or `AsBytes`, that SIMD type is also -// `FromZeroes`, `FromBytes`, or `AsBytes` respectively. +// `TryFromBytes`, `FromZeroes`, `FromBytes`, or `AsBytes`, that SIMD type is +// also `TryFromBytes`, `FromZeroes`, `FromBytes`, or `AsBytes` respectively. // - Since no upper bound is placed on the alignment, no SIMD type can be // guaranteed to be `Unaligned`. // @@ -1476,17 +3639,19 @@ safety_comment! { // // See issue #38 [2]. While this behavior is not technically guaranteed, the // likelihood that the behavior will change such that SIMD types are no longer -// `FromZeroes`, `FromBytes`, or `AsBytes` is next to zero, as that would defeat -// the entire purpose of SIMD types. Nonetheless, we put this behavior behind -// the `simd` Cargo feature, which requires consumers to opt into this stability -// hazard. +// `TryFromBytes`, `FromZeroes`, `FromBytes`, or `AsBytes` is next to zero, as +// that would defeat the entire purpose of SIMD types. Nonetheless, we put this +// behavior behind the `simd` Cargo feature, which requires consumers to opt +// into this stability hazard. // // [1] https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html // [2] https://github.com/rust-lang/unsafe-code-guidelines/issues/38 #[cfg(feature = "simd")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "simd")))] mod simd { - /// Defines a module which implements `FromZeroes`, `FromBytes`, and - /// `AsBytes` for a set of types from a module in `core::arch`. + /// Defines a module which implements `TryFromBytes`, `FromZeroes`, + /// `FromBytes`, and `AsBytes` for a set of types from a module in + /// `core::arch`. /// /// `$arch` is both the name of the defined module and the name of the /// module in `core::arch`, and `$typ` is the list of items from that module @@ -1495,8 +3660,10 @@ mod simd { // target/feature combinations don't emit any impls // and thus don't use this macro. macro_rules! simd_arch_mod { - ($arch:ident, $($typ:ident),*) => { - mod $arch { + (#[cfg $cfg:tt] $arch:ident, $mod:ident, $($typ:ident),*) => { + #[cfg $cfg] + #[cfg_attr(doc_cfg, doc(cfg $cfg))] + mod $mod { use core::arch::$arch::{$($typ),*}; use crate::*; @@ -1504,54 +3671,59 @@ mod simd { safety_comment! { /// SAFETY: /// See comment on module definition for justification. - $( unsafe_impl!($typ: FromZeroes, FromBytes, AsBytes); )* + $( unsafe_impl!($typ: TryFromBytes, FromZeroes, FromBytes, AsBytes); )* } } }; } - #[cfg(target_arch = "x86")] - simd_arch_mod!(x86, __m128, __m128d, __m128i, __m256, __m256d, __m256i); - #[cfg(target_arch = "x86_64")] - simd_arch_mod!(x86_64, __m128, __m128d, __m128i, __m256, __m256d, __m256i); - #[cfg(target_arch = "wasm32")] - simd_arch_mod!(wasm32, v128); - #[cfg(all(feature = "simd-nightly", target_arch = "powerpc"))] - simd_arch_mod!( - powerpc, - vector_bool_long, - vector_double, - vector_signed_long, - vector_unsigned_long - ); - #[cfg(all(feature = "simd-nightly", target_arch = "powerpc64"))] - simd_arch_mod!( - powerpc64, - vector_bool_long, - vector_double, - vector_signed_long, - vector_unsigned_long - ); - #[cfg(target_arch = "aarch64")] - #[rustfmt::skip] - simd_arch_mod!( - aarch64, float32x2_t, float32x4_t, float64x1_t, float64x2_t, int8x8_t, int8x8x2_t, - int8x8x3_t, int8x8x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int16x4_t, - int16x8_t, int32x2_t, int32x4_t, int64x1_t, int64x2_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, - poly8x8x4_t, poly8x16_t, poly8x16x2_t, poly8x16x3_t, poly8x16x4_t, poly16x4_t, poly16x8_t, - poly64x1_t, poly64x2_t, uint8x8_t, uint8x8x2_t, uint8x8x3_t, uint8x8x4_t, uint8x16_t, - uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint16x4_t, uint16x8_t, uint32x2_t, uint32x4_t, - uint64x1_t, uint64x2_t - ); - #[cfg(all(feature = "simd-nightly", target_arch = "arm"))] #[rustfmt::skip] - simd_arch_mod!(arm, int8x4_t, uint8x4_t); + const _: () = { + simd_arch_mod!( + #[cfg(target_arch = "x86")] + x86, x86, __m128, __m128d, __m128i, __m256, __m256d, __m256i + ); + simd_arch_mod!( + #[cfg(all(feature = "simd-nightly", target_arch = "x86"))] + x86, x86_nightly, __m512bh, __m512, __m512d, __m512i + ); + simd_arch_mod!( + #[cfg(target_arch = "x86_64")] + x86_64, x86_64, __m128, __m128d, __m128i, __m256, __m256d, __m256i + ); + simd_arch_mod!( + #[cfg(all(feature = "simd-nightly", target_arch = "x86_64"))] + x86_64, x86_64_nightly, __m512bh, __m512, __m512d, __m512i + ); + simd_arch_mod!( + #[cfg(target_arch = "wasm32")] + wasm32, wasm32, v128 + ); + simd_arch_mod!( + #[cfg(all(feature = "simd-nightly", target_arch = "powerpc"))] + powerpc, powerpc, vector_bool_long, vector_double, vector_signed_long, vector_unsigned_long + ); + simd_arch_mod!( + #[cfg(all(feature = "simd-nightly", target_arch = "powerpc64"))] + powerpc64, powerpc64, vector_bool_long, vector_double, vector_signed_long, vector_unsigned_long + ); + simd_arch_mod!( + #[cfg(target_arch = "aarch64")] + aarch64, aarch64, float32x2_t, float32x4_t, float64x1_t, float64x2_t, int8x8_t, int8x8x2_t, + int8x8x3_t, int8x8x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int16x4_t, + int16x8_t, int32x2_t, int32x4_t, int64x1_t, int64x2_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, + poly8x8x4_t, poly8x16_t, poly8x16x2_t, poly8x16x3_t, poly8x16x4_t, poly16x4_t, poly16x8_t, + poly64x1_t, poly64x2_t, uint8x8_t, uint8x8x2_t, uint8x8x3_t, uint8x8x4_t, uint8x16_t, + uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint16x4_t, uint16x8_t, uint32x2_t, uint32x4_t, + uint64x1_t, uint64x2_t + ); + simd_arch_mod!( + #[cfg(all(feature = "simd-nightly", target_arch = "arm"))] + arm, arm, int8x4_t, uint8x4_t + ); + }; } -// Used in `transmute!` below. -#[doc(hidden)] -pub use core::mem::transmute as __real_transmute; - /// Safely transmutes a value of one type to a value of another type of the same /// size. /// @@ -1563,6 +3735,17 @@ pub use core::mem::transmute as __real_transmute; /// Note that the `T` produced by the expression `$e` will *not* be dropped. /// Semantically, its bits will be copied into a new value of type `U`, the /// original `T` will be forgotten, and the value of type `U` will be returned. +/// +/// # Examples +/// +/// ``` +/// # use zerocopy::transmute; +/// let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7]; +/// +/// let two_dimensional: [[u8; 4]; 2] = transmute!(one_dimensional); +/// +/// assert_eq!(two_dimensional, [[0, 1, 2, 3], [4, 5, 6, 7]]); +/// ``` #[macro_export] macro_rules! transmute { ($e:expr) => {{ @@ -1576,28 +3759,320 @@ macro_rules! transmute { // This branch, though never taken, ensures that the type of `e` is // `AsBytes` and that the type of this macro invocation expression // is `FromBytes`. - const fn transmute<T: $crate::AsBytes, U: $crate::FromBytes>(_t: T) -> U { - unreachable!() - } - transmute(e) + + struct AssertIsAsBytes<T: $crate::AsBytes>(T); + let _ = AssertIsAsBytes(e); + + struct AssertIsFromBytes<U: $crate::FromBytes>(U); + #[allow(unused, unreachable_code)] + let u = AssertIsFromBytes(loop {}); + u.0 } else { // SAFETY: `core::mem::transmute` ensures that the type of `e` and // the type of this macro invocation expression have the same size. // We know this transmute is safe thanks to the `AsBytes` and // `FromBytes` bounds enforced by the `false` branch. // - // We use `$crate::__real_transmute` because we know it will always - // be available for crates which are using the 2015 edition of Rust. - // By contrast, if we were to use `std::mem::transmute`, this macro - // would not work for such crates in `no_std` contexts, and if we - // were to use `core::mem::transmute`, this macro would not work in - // `std` contexts in which `core` was not manually imported. This is - // not a problem for 2018 edition crates. - unsafe { $crate::__real_transmute(e) } + // We use this reexport of `core::mem::transmute` because we know it + // will always be available for crates which are using the 2015 + // edition of Rust. By contrast, if we were to use + // `std::mem::transmute`, this macro would not work for such crates + // in `no_std` contexts, and if we were to use + // `core::mem::transmute`, this macro would not work in `std` + // contexts in which `core` was not manually imported. This is not a + // problem for 2018 edition crates. + unsafe { + // Clippy: It's okay to transmute a type to itself. + #[allow(clippy::useless_transmute)] + $crate::macro_util::core_reexport::mem::transmute(e) + } + } + }} +} + +/// Safely transmutes a mutable or immutable reference of one type to an +/// immutable reference of another type of the same size. +/// +/// The expression `$e` must have a concrete type, `&T` or `&mut T`, where `T: +/// Sized + AsBytes`. The `transmute_ref!` expression must also have a concrete +/// type, `&U` (`U` is inferred from the calling context), where `U: Sized + +/// FromBytes`. It must be the case that `align_of::<T>() >= align_of::<U>()`. +/// +/// The lifetime of the input type, `&T` or `&mut T`, must be the same as or +/// outlive the lifetime of the output type, `&U`. +/// +/// # Examples +/// +/// ``` +/// # use zerocopy::transmute_ref; +/// let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7]; +/// +/// let two_dimensional: &[[u8; 4]; 2] = transmute_ref!(&one_dimensional); +/// +/// assert_eq!(two_dimensional, &[[0, 1, 2, 3], [4, 5, 6, 7]]); +/// ``` +/// +/// # Alignment increase error message +/// +/// Because of limitations on macros, the error message generated when +/// `transmute_ref!` is used to transmute from a type of lower alignment to a +/// type of higher alignment is somewhat confusing. For example, the following +/// code: +/// +/// ```compile_fail +/// const INCREASE_ALIGNMENT: &u16 = zerocopy::transmute_ref!(&[0u8; 2]); +/// ``` +/// +/// ...generates the following error: +/// +/// ```text +/// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +/// --> src/lib.rs:1524:34 +/// | +/// 5 | const INCREASE_ALIGNMENT: &u16 = zerocopy::transmute_ref!(&[0u8; 2]); +/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +/// | +/// = note: source type: `AlignOf<[u8; 2]>` (8 bits) +/// = note: target type: `MaxAlignsOf<[u8; 2], u16>` (16 bits) +/// = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) +/// ``` +/// +/// This is saying that `max(align_of::<T>(), align_of::<U>()) != +/// align_of::<T>()`, which is equivalent to `align_of::<T>() < +/// align_of::<U>()`. +#[macro_export] +macro_rules! transmute_ref { + ($e:expr) => {{ + // NOTE: This must be a macro (rather than a function with trait bounds) + // because there's no way, in a generic context, to enforce that two + // types have the same size or alignment. + + // Ensure that the source type is a reference or a mutable reference + // (note that mutable references are implicitly reborrowed here). + let e: &_ = $e; + + #[allow(unused, clippy::diverging_sub_expression)] + if false { + // This branch, though never taken, ensures that the type of `e` is + // `&T` where `T: 't + Sized + AsBytes`, that the type of this macro + // expression is `&U` where `U: 'u + Sized + FromBytes`, and that + // `'t` outlives `'u`. + + struct AssertIsAsBytes<'a, T: ::core::marker::Sized + $crate::AsBytes>(&'a T); + let _ = AssertIsAsBytes(e); + + struct AssertIsFromBytes<'a, U: ::core::marker::Sized + $crate::FromBytes>(&'a U); + #[allow(unused, unreachable_code)] + let u = AssertIsFromBytes(loop {}); + u.0 + } else if false { + // This branch, though never taken, ensures that `size_of::<T>() == + // size_of::<U>()` and that that `align_of::<T>() >= + // align_of::<U>()`. + + // `t` is inferred to have type `T` because it's assigned to `e` (of + // type `&T`) as `&t`. + let mut t = unreachable!(); + e = &t; + + // `u` is inferred to have type `U` because it's used as `&u` as the + // value returned from this branch. + let u; + + $crate::assert_size_eq!(t, u); + $crate::assert_align_gt_eq!(t, u); + + &u + } else { + // SAFETY: For source type `Src` and destination type `Dst`: + // - We know that `Src: AsBytes` and `Dst: FromBytes` thanks to the + // uses of `AssertIsAsBytes` and `AssertIsFromBytes` above. + // - We know that `size_of::<Src>() == size_of::<Dst>()` thanks to + // the use of `assert_size_eq!` above. + // - We know that `align_of::<Src>() >= align_of::<Dst>()` thanks to + // the use of `assert_align_gt_eq!` above. + unsafe { $crate::macro_util::transmute_ref(e) } + } + }} +} + +/// Safely transmutes a mutable reference of one type to an mutable reference of +/// another type of the same size. +/// +/// The expression `$e` must have a concrete type, `&mut T`, where `T: Sized + +/// AsBytes`. The `transmute_mut!` expression must also have a concrete type, +/// `&mut U` (`U` is inferred from the calling context), where `U: Sized + +/// FromBytes`. It must be the case that `align_of::<T>() >= align_of::<U>()`. +/// +/// The lifetime of the input type, `&mut T`, must be the same as or outlive the +/// lifetime of the output type, `&mut U`. +/// +/// # Examples +/// +/// ``` +/// # use zerocopy::transmute_mut; +/// let mut one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7]; +/// +/// let two_dimensional: &mut [[u8; 4]; 2] = transmute_mut!(&mut one_dimensional); +/// +/// assert_eq!(two_dimensional, &[[0, 1, 2, 3], [4, 5, 6, 7]]); +/// +/// two_dimensional.reverse(); +/// +/// assert_eq!(one_dimensional, [4, 5, 6, 7, 0, 1, 2, 3]); +/// ``` +/// +/// # Alignment increase error message +/// +/// Because of limitations on macros, the error message generated when +/// `transmute_mut!` is used to transmute from a type of lower alignment to a +/// type of higher alignment is somewhat confusing. For example, the following +/// code: +/// +/// ```compile_fail +/// const INCREASE_ALIGNMENT: &mut u16 = zerocopy::transmute_mut!(&mut [0u8; 2]); +/// ``` +/// +/// ...generates the following error: +/// +/// ```text +/// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +/// --> src/lib.rs:1524:34 +/// | +/// 5 | const INCREASE_ALIGNMENT: &mut u16 = zerocopy::transmute_mut!(&mut [0u8; 2]); +/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +/// | +/// = note: source type: `AlignOf<[u8; 2]>` (8 bits) +/// = note: target type: `MaxAlignsOf<[u8; 2], u16>` (16 bits) +/// = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) +/// ``` +/// +/// This is saying that `max(align_of::<T>(), align_of::<U>()) != +/// align_of::<T>()`, which is equivalent to `align_of::<T>() < +/// align_of::<U>()`. +#[macro_export] +macro_rules! transmute_mut { + ($e:expr) => {{ + // NOTE: This must be a macro (rather than a function with trait bounds) + // because there's no way, in a generic context, to enforce that two + // types have the same size or alignment. + + // Ensure that the source type is a mutable reference. + let e: &mut _ = $e; + + #[allow(unused, clippy::diverging_sub_expression)] + if false { + // This branch, though never taken, ensures that the type of `e` is + // `&mut T` where `T: 't + Sized + FromBytes + AsBytes`, that the + // type of this macro expression is `&mut U` where `U: 'u + Sized + + // FromBytes + AsBytes`. + + // We use immutable references here rather than mutable so that, if + // this macro is used in a const context (in which, as of this + // writing, mutable references are banned), the error message + // appears to originate in the user's code rather than in the + // internals of this macro. + struct AssertSrcIsFromBytes<'a, T: ::core::marker::Sized + $crate::FromBytes>(&'a T); + struct AssertSrcIsAsBytes<'a, T: ::core::marker::Sized + $crate::AsBytes>(&'a T); + struct AssertDstIsFromBytes<'a, T: ::core::marker::Sized + $crate::FromBytes>(&'a T); + struct AssertDstIsAsBytes<'a, T: ::core::marker::Sized + $crate::AsBytes>(&'a T); + + if true { + let _ = AssertSrcIsFromBytes(&*e); + } else { + let _ = AssertSrcIsAsBytes(&*e); + } + + if true { + #[allow(unused, unreachable_code)] + let u = AssertDstIsFromBytes(loop {}); + &mut *u.0 + } else { + #[allow(unused, unreachable_code)] + let u = AssertDstIsAsBytes(loop {}); + &mut *u.0 + } + } else if false { + // This branch, though never taken, ensures that `size_of::<T>() == + // size_of::<U>()` and that that `align_of::<T>() >= + // align_of::<U>()`. + + // `t` is inferred to have type `T` because it's assigned to `e` (of + // type `&mut T`) as `&mut t`. + let mut t = unreachable!(); + e = &mut t; + + // `u` is inferred to have type `U` because it's used as `&mut u` as + // the value returned from this branch. + let u; + + $crate::assert_size_eq!(t, u); + $crate::assert_align_gt_eq!(t, u); + + &mut u + } else { + // SAFETY: For source type `Src` and destination type `Dst`: + // - We know that `Src: FromBytes + AsBytes` and `Dst: FromBytes + + // AsBytes` thanks to the uses of `AssertSrcIsFromBytes`, + // `AssertSrcIsAsBytes`, `AssertDstIsFromBytes`, and + // `AssertDstIsAsBytes` above. + // - We know that `size_of::<Src>() == size_of::<Dst>()` thanks to + // the use of `assert_size_eq!` above. + // - We know that `align_of::<Src>() >= align_of::<Dst>()` thanks to + // the use of `assert_align_gt_eq!` above. + unsafe { $crate::macro_util::transmute_mut(e) } } }} } +/// Includes a file and safely transmutes it to a value of an arbitrary type. +/// +/// The file will be included as a byte array, `[u8; N]`, which will be +/// transmuted to another type, `T`. `T` is inferred from the calling context, +/// and must implement [`FromBytes`]. +/// +/// The file is located relative to the current file (similarly to how modules +/// are found). The provided path is interpreted in a platform-specific way at +/// compile time. So, for instance, an invocation with a Windows path containing +/// backslashes `\` would not compile correctly on Unix. +/// +/// `include_value!` is ignorant of byte order. For byte order-aware types, see +/// the [`byteorder`] module. +/// +/// # Examples +/// +/// Assume there are two files in the same directory with the following +/// contents: +/// +/// File `data` (no trailing newline): +/// +/// ```text +/// abcd +/// ``` +/// +/// File `main.rs`: +/// +/// ```rust +/// use zerocopy::include_value; +/// # macro_rules! include_value { +/// # ($file:expr) => { zerocopy::include_value!(concat!("../testdata/include_value/", $file)) }; +/// # } +/// +/// fn main() { +/// let as_u32: u32 = include_value!("data"); +/// assert_eq!(as_u32, u32::from_ne_bytes([b'a', b'b', b'c', b'd'])); +/// let as_i32: i32 = include_value!("data"); +/// assert_eq!(as_i32, i32::from_ne_bytes([b'a', b'b', b'c', b'd'])); +/// } +/// ``` +#[doc(alias("include_bytes", "include_data", "include_type"))] +#[macro_export] +macro_rules! include_value { + ($file:expr $(,)?) => { + $crate::transmute!(*::core::include_bytes!($file)) + }; +} + /// A typed reference derived from a byte slice. /// /// A `Ref<B, T>` is a reference to a `T` which is stored in a byte slice, `B`. @@ -1775,10 +4250,8 @@ where Some(len) => len, None => return None, }; - if bytes.len() < expected_len { - return None; - } - let (bytes, suffix) = bytes.split_at(expected_len); + let split_at = bytes.len().checked_sub(expected_len)?; + let (bytes, suffix) = bytes.split_at(split_at); Self::new_slice(suffix).map(move |l| (bytes, l)) } } @@ -2254,7 +4727,7 @@ where /// and no mutable references to the same memory may be constructed during /// `'a`. unsafe fn deref_helper<'a>(&self) -> &'a T { - // TODO(#61): Add a "SAFETY" comment and remove this `allow`. + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe { &*self.0.as_ptr().cast::<T>() @@ -2279,7 +4752,7 @@ where /// and no other references - mutable or immutable - to the same memory may /// be constructed during `'a`. unsafe fn deref_mut_helper<'a>(&mut self) -> &'a mut T { - // TODO(#61): Add a "SAFETY" comment and remove this `allow`. + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe { &mut *self.0.as_mut_ptr().cast::<T>() @@ -2308,7 +4781,7 @@ where debug_assert_eq!(len % elem_size, 0); len / elem_size }; - // TODO(#61): Add a "SAFETY" comment and remove this `allow`. + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe { slice::from_raw_parts(self.0.as_ptr().cast::<T>(), elems) @@ -2338,7 +4811,7 @@ where debug_assert_eq!(len % elem_size, 0); len / elem_size }; - // TODO(#61): Add a "SAFETY" comment and remove this `allow`. + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe { slice::from_raw_parts_mut(self.0.as_mut_ptr().cast::<T>(), elems) @@ -2612,7 +5085,6 @@ where mod sealed { pub trait ByteSliceSealed {} - pub trait KnownLayoutSealed {} } // ByteSlice and ByteSliceMut abstract over [u8] references (&[u8], &mut [u8], @@ -2680,7 +5152,7 @@ pub unsafe trait ByteSliceMut: ByteSlice + DerefMut { } impl<'a> sealed::ByteSliceSealed for &'a [u8] {} -// TODO(#61): Add a "SAFETY" comment and remove this `allow`. +// TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe impl<'a> ByteSlice for &'a [u8] { #[inline] @@ -2690,7 +5162,7 @@ unsafe impl<'a> ByteSlice for &'a [u8] { } impl<'a> sealed::ByteSliceSealed for &'a mut [u8] {} -// TODO(#61): Add a "SAFETY" comment and remove this `allow`. +// TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe impl<'a> ByteSlice for &'a mut [u8] { #[inline] @@ -2700,7 +5172,7 @@ unsafe impl<'a> ByteSlice for &'a mut [u8] { } impl<'a> sealed::ByteSliceSealed for cell::Ref<'a, [u8]> {} -// TODO(#61): Add a "SAFETY" comment and remove this `allow`. +// TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> { #[inline] @@ -2710,7 +5182,7 @@ unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> { } impl<'a> sealed::ByteSliceSealed for RefMut<'a, [u8]> {} -// TODO(#61): Add a "SAFETY" comment and remove this `allow`. +// TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> { #[inline] @@ -2719,15 +5191,16 @@ unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> { } } -// TODO(#61): Add a "SAFETY" comment and remove this `allow`. +// TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe impl<'a> ByteSliceMut for &'a mut [u8] {} -// TODO(#61): Add a "SAFETY" comment and remove this `allow`. +// TODO(#429): Add a "SAFETY" comment and remove this `allow`. #[allow(clippy::undocumented_unsafe_blocks)] unsafe impl<'a> ByteSliceMut for RefMut<'a, [u8]> {} #[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] mod alloc_support { use alloc::vec::Vec; @@ -2775,6 +5248,8 @@ mod alloc_support { #[cfg(test)] mod tests { + use core::convert::TryFrom as _; + use super::*; #[test] @@ -2950,7 +5425,7 @@ pub use alloc_support::*; mod tests { #![allow(clippy::unreadable_literal)] - use core::ops::Deref; + use core::{cell::UnsafeCell, convert::TryInto as _, ops::Deref}; use static_assertions::assert_impl_all; @@ -2978,23 +5453,212 @@ mod tests { } } + /// Tests of when a sized `DstLayout` is extended with a sized field. + #[allow(clippy::decimal_literal_representation)] + #[test] + fn test_dst_layout_extend_sized_with_sized() { + // This macro constructs a layout corresponding to a `u8` and extends it + // with a zero-sized trailing field of given alignment `n`. The macro + // tests that the resulting layout has both size and alignment `min(n, + // P)` for all valid values of `repr(packed(P))`. + macro_rules! test_align_is_size { + ($n:expr) => { + let base = DstLayout::for_type::<u8>(); + let trailing_field = DstLayout::for_type::<elain::Align<$n>>(); + + let packs = + core::iter::once(None).chain((0..29).map(|p| NonZeroUsize::new(2usize.pow(p)))); + + for pack in packs { + let composite = base.extend(trailing_field, pack); + let max_align = pack.unwrap_or(DstLayout::CURRENT_MAX_ALIGN); + let align = $n.min(max_align.get()); + assert_eq!( + composite, + DstLayout { + align: NonZeroUsize::new(align).unwrap(), + size_info: SizeInfo::Sized { _size: align } + } + ) + } + }; + } + + test_align_is_size!(1); + test_align_is_size!(2); + test_align_is_size!(4); + test_align_is_size!(8); + test_align_is_size!(16); + test_align_is_size!(32); + test_align_is_size!(64); + test_align_is_size!(128); + test_align_is_size!(256); + test_align_is_size!(512); + test_align_is_size!(1024); + test_align_is_size!(2048); + test_align_is_size!(4096); + test_align_is_size!(8192); + test_align_is_size!(16384); + test_align_is_size!(32768); + test_align_is_size!(65536); + test_align_is_size!(131072); + test_align_is_size!(262144); + test_align_is_size!(524288); + test_align_is_size!(1048576); + test_align_is_size!(2097152); + test_align_is_size!(4194304); + test_align_is_size!(8388608); + test_align_is_size!(16777216); + test_align_is_size!(33554432); + test_align_is_size!(67108864); + test_align_is_size!(33554432); + test_align_is_size!(134217728); + test_align_is_size!(268435456); + } + + /// Tests of when a sized `DstLayout` is extended with a DST field. + #[test] + fn test_dst_layout_extend_sized_with_dst() { + // Test that for all combinations of real-world alignments and + // `repr_packed` values, that the extension of a sized `DstLayout`` with + // a DST field correctly computes the trailing offset in the composite + // layout. + + let aligns = (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap()); + let packs = core::iter::once(None).chain(aligns.clone().map(Some)); + + for align in aligns { + for pack in packs.clone() { + let base = DstLayout::for_type::<u8>(); + let elem_size = 42; + let trailing_field_offset = 11; + + let trailing_field = DstLayout { + align, + size_info: SizeInfo::SliceDst(TrailingSliceLayout { + _elem_size: elem_size, + _offset: 11, + }), + }; + + let composite = base.extend(trailing_field, pack); + + let max_align = pack.unwrap_or(DstLayout::CURRENT_MAX_ALIGN).get(); + + let align = align.get().min(max_align); + + assert_eq!( + composite, + DstLayout { + align: NonZeroUsize::new(align).unwrap(), + size_info: SizeInfo::SliceDst(TrailingSliceLayout { + _elem_size: elem_size, + _offset: align + trailing_field_offset, + }), + } + ) + } + } + } + + /// Tests that calling `pad_to_align` on a sized `DstLayout` adds the + /// expected amount of trailing padding. + #[test] + fn test_dst_layout_pad_to_align_with_sized() { + // For all valid alignments `align`, construct a one-byte layout aligned + // to `align`, call `pad_to_align`, and assert that the size of the + // resulting layout is equal to `align`. + for align in (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap()) { + let layout = DstLayout { align, size_info: SizeInfo::Sized { _size: 1 } }; + + assert_eq!( + layout.pad_to_align(), + DstLayout { align, size_info: SizeInfo::Sized { _size: align.get() } } + ); + } + + // Test explicitly-provided combinations of unpadded and padded + // counterparts. + + macro_rules! test { + (unpadded { size: $unpadded_size:expr, align: $unpadded_align:expr } + => padded { size: $padded_size:expr, align: $padded_align:expr }) => { + let unpadded = DstLayout { + align: NonZeroUsize::new($unpadded_align).unwrap(), + size_info: SizeInfo::Sized { _size: $unpadded_size }, + }; + let padded = unpadded.pad_to_align(); + + assert_eq!( + padded, + DstLayout { + align: NonZeroUsize::new($padded_align).unwrap(), + size_info: SizeInfo::Sized { _size: $padded_size }, + } + ); + }; + } + + test!(unpadded { size: 0, align: 4 } => padded { size: 0, align: 4 }); + test!(unpadded { size: 1, align: 4 } => padded { size: 4, align: 4 }); + test!(unpadded { size: 2, align: 4 } => padded { size: 4, align: 4 }); + test!(unpadded { size: 3, align: 4 } => padded { size: 4, align: 4 }); + test!(unpadded { size: 4, align: 4 } => padded { size: 4, align: 4 }); + test!(unpadded { size: 5, align: 4 } => padded { size: 8, align: 4 }); + test!(unpadded { size: 6, align: 4 } => padded { size: 8, align: 4 }); + test!(unpadded { size: 7, align: 4 } => padded { size: 8, align: 4 }); + test!(unpadded { size: 8, align: 4 } => padded { size: 8, align: 4 }); + + let current_max_align = DstLayout::CURRENT_MAX_ALIGN.get(); + + test!(unpadded { size: 1, align: current_max_align } + => padded { size: current_max_align, align: current_max_align }); + + test!(unpadded { size: current_max_align + 1, align: current_max_align } + => padded { size: current_max_align * 2, align: current_max_align }); + } + + /// Tests that calling `pad_to_align` on a DST `DstLayout` is a no-op. + #[test] + fn test_dst_layout_pad_to_align_with_dst() { + for align in (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap()) { + for offset in 0..10 { + for elem_size in 0..10 { + let layout = DstLayout { + align, + size_info: SizeInfo::SliceDst(TrailingSliceLayout { + _offset: offset, + _elem_size: elem_size, + }), + }; + assert_eq!(layout.pad_to_align(), layout); + } + } + } + } + // This test takes a long time when running under Miri, so we skip it in // that case. This is acceptable because this is a logic test that doesn't // attempt to expose UB. #[test] #[cfg_attr(miri, ignore)] - fn test_validate_cast_and_convert_metadata() { - fn layout( - base_size: usize, - align: usize, - _trailing_slice_elem_size: Option<usize>, - ) -> DstLayout { - DstLayout { - _base_layout: Layout::from_size_align(base_size, align).unwrap(), - _trailing_slice_elem_size, + fn testvalidate_cast_and_convert_metadata() { + impl From<usize> for SizeInfo { + fn from(_size: usize) -> SizeInfo { + SizeInfo::Sized { _size } + } + } + + impl From<(usize, usize)> for SizeInfo { + fn from((_offset, _elem_size): (usize, usize)) -> SizeInfo { + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) } } + fn layout<S: Into<SizeInfo>>(s: S, align: usize) -> DstLayout { + DstLayout { size_info: s.into(), align: NonZeroUsize::new(align).unwrap() } + } + /// This macro accepts arguments in the form of: /// /// layout(_, _, _).validate(_, _, _), Ok(Some((_, _))) @@ -3032,30 +5696,64 @@ mod tests { /// `a..b`). In this case, wrap the expression in parentheses, and it /// will become valid `tt`. macro_rules! test { - ( - layout($base_size:tt, $align:tt, $trailing_size:tt) + ($(:$sizes:expr =>)? + layout($size:tt, $align:tt) .validate($addr:tt, $bytes_len:tt, $cast_type:tt), $expect:pat $(,)? ) => { itertools::iproduct!( - test!(@generate_usize $base_size), + test!(@generate_size $size), test!(@generate_align $align), - test!(@generate_opt_usize $trailing_size), test!(@generate_usize $addr), test!(@generate_usize $bytes_len), test!(@generate_cast_type $cast_type) - ).for_each(|(base_size, align, trailing_size, addr, bytes_len, cast_type)| { + ).for_each(|(size_info, align, addr, bytes_len, cast_type)| { + // Temporarily disable the panic hook installed by the test + // harness. If we don't do this, all panic messages will be + // kept in an internal log. On its own, this isn't a + // problem, but if a non-caught panic ever happens (ie, in + // code later in this test not in this macro), all of the + // previously-buffered messages will be dumped, hiding the + // real culprit. + let previous_hook = std::panic::take_hook(); + // I don't understand why, but this seems to be required in + // addition to the previous line. + std::panic::set_hook(Box::new(|_| {})); let actual = std::panic::catch_unwind(|| { - layout(base_size, align, trailing_size)._validate_cast_and_convert_metadata(addr, bytes_len, cast_type) + layout(size_info, align).validate_cast_and_convert_metadata(addr, bytes_len, cast_type) }).map_err(|d| { *d.downcast::<&'static str>().expect("expected string panic message").as_ref() }); + std::panic::set_hook(previous_hook); + assert_matches::assert_matches!( actual, $expect, - "layout({base_size}, {align}, {trailing_size:?}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?})", + "layout({size_info:?}, {align}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?})", ); }); }; (@generate_usize _) => { 0..8 }; + // Generate sizes for both Sized and !Sized types. + (@generate_size _) => { + test!(@generate_size (_)).chain(test!(@generate_size (_, _))) + }; + // Generate sizes for both Sized and !Sized types by chaining + // specified iterators for each. + (@generate_size ($sized_sizes:tt | $unsized_sizes:tt)) => { + test!(@generate_size ($sized_sizes)).chain(test!(@generate_size $unsized_sizes)) + }; + // Generate sizes for Sized types. + (@generate_size (_)) => { test!(@generate_size (0..8)) }; + (@generate_size ($sizes:expr)) => { $sizes.into_iter().map(Into::<SizeInfo>::into) }; + // Generate sizes for !Sized types. + (@generate_size ($min_sizes:tt, $elem_sizes:tt)) => { + itertools::iproduct!( + test!(@generate_min_size $min_sizes), + test!(@generate_elem_size $elem_sizes) + ).map(Into::<SizeInfo>::into) + }; + (@generate_fixed_size _) => { (0..8).into_iter().map(Into::<SizeInfo>::into) }; + (@generate_min_size _) => { 0..8 }; + (@generate_elem_size _) => { 1..8 }; (@generate_align _) => { [1, 2, 4, 8, 16] }; (@generate_opt_usize _) => { [None].into_iter().chain((0..8).map(Some).into_iter()) }; (@generate_cast_type _) => { [_CastType::_Prefix, _CastType::_Suffix] }; @@ -3068,21 +5766,20 @@ mod tests { (@$_:ident $vals:expr) => { $vals }; } - const EVENS: [usize; 5] = [0, 2, 4, 6, 8]; - const NZ_EVENS: [usize; 5] = [2, 4, 6, 8, 10]; - const ODDS: [usize; 5] = [1, 3, 5, 7, 9]; + const EVENS: [usize; 8] = [0, 2, 4, 6, 8, 10, 12, 14]; + const ODDS: [usize; 8] = [1, 3, 5, 7, 9, 11, 13, 15]; // base_size is too big for the memory region. - test!(layout((1..8), _, ((1..8).map(Some))).validate(_, [0], _), Ok(None)); - test!(layout((2..8), _, ((1..8).map(Some))).validate(_, [1], _), Ok(None)); + test!(layout(((1..8) | ((1..8), (1..8))), _).validate(_, [0], _), Ok(None)); + test!(layout(((2..8) | ((2..8), (2..8))), _).validate(_, [1], _), Ok(None)); // addr is unaligned for prefix cast - test!(layout(_, [2], [None]).validate(ODDS, _, _Prefix), Ok(None)); - test!(layout(_, [2], (NZ_EVENS.map(Some))).validate(ODDS, _, _Prefix), Ok(None)); + test!(layout(_, [2]).validate(ODDS, _, _Prefix), Ok(None)); + test!(layout(_, [2]).validate(ODDS, _, _Prefix), Ok(None)); // addr is aligned, but end of buffer is unaligned for suffix cast - test!(layout(_, [2], [None]).validate(EVENS, ODDS, _Suffix), Ok(None)); - test!(layout(_, [2], (NZ_EVENS.map(Some))).validate(EVENS, ODDS, _Suffix), Ok(None)); + test!(layout(_, [2]).validate(EVENS, ODDS, _Suffix), Ok(None)); + test!(layout(_, [2]).validate(EVENS, ODDS, _Suffix), Ok(None)); // Unfortunately, these constants cannot easily be used in the // implementation of `validate_cast_and_convert_metadata`, since @@ -3100,16 +5797,13 @@ mod tests { } // casts with ZST trailing element types are unsupported - test!(layout(_, _, [Some(0)]).validate(_, _, _), Err(msgs::TRAILING),); + test!(layout((_, [0]), _).validate(_, _, _), Err(msgs::TRAILING),); // addr + bytes_len must not overflow usize + test!(layout(_, _).validate([usize::MAX], (1..100), _), Err(msgs::OVERFLOW)); + test!(layout(_, _).validate((1..100), [usize::MAX], _), Err(msgs::OVERFLOW)); test!( - layout(_, [1], (NZ_EVENS.map(Some))).validate([usize::MAX], (1..100), _), - Err(msgs::OVERFLOW) - ); - test!(layout(_, [1], [None]).validate((1..100), [usize::MAX], _), Err(msgs::OVERFLOW)); - test!( - layout([1], [1], [None]).validate( + layout(_, _).validate( [usize::MAX / 2 + 1, usize::MAX], [usize::MAX / 2 + 1, usize::MAX], _ @@ -3124,37 +5818,44 @@ mod tests { (layout, addr, bytes_len, cast_type): (DstLayout, usize, usize, _CastType), ) { if let Some((elems, split_at)) = - layout._validate_cast_and_convert_metadata(addr, bytes_len, cast_type) + layout.validate_cast_and_convert_metadata(addr, bytes_len, cast_type) { - let (base_size, align, trailing_elem_size) = ( - layout._base_layout.size(), - layout._base_layout.align(), - layout._trailing_slice_elem_size, - ); - + let (size_info, align) = (layout.size_info, layout.align); let debug_str = format!( - "layout({base_size}, {align}, {trailing_elem_size:?}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?}) => ({elems}, {split_at})", + "layout({size_info:?}, {align}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?}) => ({elems}, {split_at})", ); // If this is a sized type (no trailing slice), then `elems` is // meaningless, but in practice we set it to 0. Callers are not // allowed to rely on this, but a lot of math is nicer if // they're able to, and some callers might accidentally do that. - assert!(!(trailing_elem_size.is_none() && elems != 0), "{}", debug_str); - - let resulting_size = base_size + (elems * trailing_elem_size.unwrap_or(0)); - // Test that, for unsized types, `validate_cast_and_convert_metadata` computed the - // largest possible value that fits in the given byte range. - assert!( - trailing_elem_size - .map(|elem_size| resulting_size + elem_size > bytes_len) - .unwrap_or(true), - "{}", - debug_str - ); + let sized = matches!(layout.size_info, SizeInfo::Sized { .. }); + assert!(!(sized && elems != 0), "{}", debug_str); + + let resulting_size = match layout.size_info { + SizeInfo::Sized { _size } => _size, + SizeInfo::SliceDst(TrailingSliceLayout { + _offset: offset, + _elem_size: elem_size, + }) => { + let padded_size = |elems| { + let without_padding = offset + elems * elem_size; + without_padding + + util::core_layout::padding_needed_for(without_padding, align) + }; + + let resulting_size = padded_size(elems); + // Test that `validate_cast_and_convert_metadata` + // computed the largest possible value that fits in the + // given range. + assert!(padded_size(elems + 1) > bytes_len, "{}", debug_str); + resulting_size + } + }; - // Test safety postconditions guaranteed by `validate_cast_and_convert_metadata`. - assert!(resulting_size <= bytes_len); + // Test safety postconditions guaranteed by + // `validate_cast_and_convert_metadata`. + assert!(resulting_size <= bytes_len, "{}", debug_str); match cast_type { _CastType::_Prefix => { assert_eq!(addr % align, 0, "{}", debug_str); @@ -3165,19 +5866,391 @@ mod tests { assert_eq!((addr + split_at) % align, 0, "{}", debug_str); } } + } else { + let min_size = match layout.size_info { + SizeInfo::Sized { _size } => _size, + SizeInfo::SliceDst(TrailingSliceLayout { _offset, .. }) => { + _offset + util::core_layout::padding_needed_for(_offset, layout.align) + } + }; + + // If a cast is invalid, it is either because... + // 1. there are insufficent bytes at the given region for type: + let insufficient_bytes = bytes_len < min_size; + // 2. performing the cast would misalign type: + let base = match cast_type { + _CastType::_Prefix => 0, + _CastType::_Suffix => bytes_len, + }; + let misaligned = (base + addr) % layout.align != 0; + + assert!(insufficient_bytes || misaligned); } } - let layouts = itertools::iproduct!(0..8, [1, 2, 4, 8], (1..8).map(Some).chain([None])) - .filter(|(size, align, trailing_elem_size)| { - size % align == 0 && trailing_elem_size.unwrap_or(*align) % align == 0 - }) - .map(|(s, a, t)| layout(s, a, t)); + let sizes = 0..8; + let elem_sizes = 1..8; + let size_infos = sizes + .clone() + .map(Into::<SizeInfo>::into) + .chain(itertools::iproduct!(sizes, elem_sizes).map(Into::<SizeInfo>::into)); + let layouts = itertools::iproduct!(size_infos, [1, 2, 4, 8, 16, 32]) + .filter(|(size_info, align)| !matches!(size_info, SizeInfo::Sized { _size } if _size % align != 0)) + .map(|(size_info, align)| layout(size_info, align)); itertools::iproduct!(layouts, 0..8, 0..8, [_CastType::_Prefix, _CastType::_Suffix]) .for_each(validate_behavior); } #[test] + #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + fn test_validate_rust_layout() { + use core::ptr::NonNull; + + // This test synthesizes pointers with various metadata and uses Rust's + // built-in APIs to confirm that Rust makes decisions about type layout + // which are consistent with what we believe is guaranteed by the + // language. If this test fails, it doesn't just mean our code is wrong + // - it means we're misunderstanding the language's guarantees. + + #[derive(Debug)] + struct MacroArgs { + offset: usize, + align: NonZeroUsize, + elem_size: Option<usize>, + } + + /// # Safety + /// + /// `test` promises to only call `addr_of_slice_field` on a `NonNull<T>` + /// which points to a valid `T`. + /// + /// `with_elems` must produce a pointer which points to a valid `T`. + fn test<T: ?Sized, W: Fn(usize) -> NonNull<T>>( + args: MacroArgs, + with_elems: W, + addr_of_slice_field: Option<fn(NonNull<T>) -> NonNull<u8>>, + ) { + let dst = args.elem_size.is_some(); + let layout = { + let size_info = match args.elem_size { + Some(elem_size) => SizeInfo::SliceDst(TrailingSliceLayout { + _offset: args.offset, + _elem_size: elem_size, + }), + None => SizeInfo::Sized { + // Rust only supports types whose sizes are a multiple + // of their alignment. If the macro created a type like + // this: + // + // #[repr(C, align(2))] + // struct Foo([u8; 1]); + // + // ...then Rust will automatically round the type's size + // up to 2. + _size: args.offset + + util::core_layout::padding_needed_for(args.offset, args.align), + }, + }; + DstLayout { size_info, align: args.align } + }; + + for elems in 0..128 { + let ptr = with_elems(elems); + + if let Some(addr_of_slice_field) = addr_of_slice_field { + let slc_field_ptr = addr_of_slice_field(ptr).as_ptr(); + // SAFETY: Both `slc_field_ptr` and `ptr` are pointers to + // the same valid Rust object. + let offset: usize = + unsafe { slc_field_ptr.byte_offset_from(ptr.as_ptr()).try_into().unwrap() }; + assert_eq!(offset, args.offset); + } + + // SAFETY: `ptr` points to a valid `T`. + let (size, align) = unsafe { + (mem::size_of_val_raw(ptr.as_ptr()), mem::align_of_val_raw(ptr.as_ptr())) + }; + + // Avoid expensive allocation when running under Miri. + let assert_msg = if !cfg!(miri) { + format!("\n{args:?}\nsize:{size}, align:{align}") + } else { + String::new() + }; + + let without_padding = + args.offset + args.elem_size.map(|elem_size| elems * elem_size).unwrap_or(0); + assert!(size >= without_padding, "{}", assert_msg); + assert_eq!(align, args.align.get(), "{}", assert_msg); + + // This encodes the most important part of the test: our + // understanding of how Rust determines the layout of repr(C) + // types. Sized repr(C) types are trivial, but DST types have + // some subtlety. Note that: + // - For sized types, `without_padding` is just the size of the + // type that we constructed for `Foo`. Since we may have + // requested a larger alignment, `Foo` may actually be larger + // than this, hence `padding_needed_for`. + // - For unsized types, `without_padding` is dynamically + // computed from the offset, the element size, and element + // count. We expect that the size of the object should be + // `offset + elem_size * elems` rounded up to the next + // alignment. + let expected_size = without_padding + + util::core_layout::padding_needed_for(without_padding, args.align); + assert_eq!(expected_size, size, "{}", assert_msg); + + // For zero-sized element types, + // `validate_cast_and_convert_metadata` just panics, so we skip + // testing those types. + if args.elem_size.map(|elem_size| elem_size > 0).unwrap_or(true) { + let addr = ptr.addr().get(); + let (got_elems, got_split_at) = layout + .validate_cast_and_convert_metadata(addr, size, _CastType::_Prefix) + .unwrap(); + // Avoid expensive allocation when running under Miri. + let assert_msg = if !cfg!(miri) { + format!( + "{}\nvalidate_cast_and_convert_metadata({addr}, {size})", + assert_msg + ) + } else { + String::new() + }; + assert_eq!(got_split_at, size, "{}", assert_msg); + if dst { + assert!(got_elems >= elems, "{}", assert_msg); + if got_elems != elems { + // If `validate_cast_and_convert_metadata` + // returned more elements than `elems`, that + // means that `elems` is not the maximum number + // of elements that can fit in `size` - in other + // words, there is enough padding at the end of + // the value to fit at least one more element. + // If we use this metadata to synthesize a + // pointer, despite having a different element + // count, we still expect it to have the same + // size. + let got_ptr = with_elems(got_elems); + // SAFETY: `got_ptr` is a pointer to a valid `T`. + let size_of_got_ptr = unsafe { mem::size_of_val_raw(got_ptr.as_ptr()) }; + assert_eq!(size_of_got_ptr, size, "{}", assert_msg); + } + } else { + // For sized casts, the returned element value is + // technically meaningless, and we don't guarantee any + // particular value. In practice, it's always zero. + assert_eq!(got_elems, 0, "{}", assert_msg) + } + } + } + } + + macro_rules! validate_against_rust { + ($offset:literal, $align:literal $(, $elem_size:literal)?) => {{ + #[repr(C, align($align))] + struct Foo([u8; $offset]$(, [[u8; $elem_size]])?); + + let args = MacroArgs { + offset: $offset, + align: $align.try_into().unwrap(), + elem_size: { + #[allow(unused)] + let ret = None::<usize>; + $(let ret = Some($elem_size);)? + ret + } + }; + + #[repr(C, align($align))] + struct FooAlign; + // Create an aligned buffer to use in order to synthesize + // pointers to `Foo`. We don't ever load values from these + // pointers - we just do arithmetic on them - so having a "real" + // block of memory as opposed to a validly-aligned-but-dangling + // pointer is only necessary to make Miri happy since we run it + // with "strict provenance" checking enabled. + let aligned_buf = Align::<_, FooAlign>::new([0u8; 1024]); + let with_elems = |elems| { + let slc = NonNull::slice_from_raw_parts(NonNull::from(&aligned_buf.t), elems); + #[allow(clippy::as_conversions)] + NonNull::new(slc.as_ptr() as *mut Foo).unwrap() + }; + let addr_of_slice_field = { + #[allow(unused)] + let f = None::<fn(NonNull<Foo>) -> NonNull<u8>>; + $( + // SAFETY: `test` promises to only call `f` with a `ptr` + // to a valid `Foo`. + let f: Option<fn(NonNull<Foo>) -> NonNull<u8>> = Some(|ptr: NonNull<Foo>| unsafe { + NonNull::new(ptr::addr_of_mut!((*ptr.as_ptr()).1)).unwrap().cast::<u8>() + }); + let _ = $elem_size; + )? + f + }; + + test::<Foo, _>(args, with_elems, addr_of_slice_field); + }}; + } + + // Every permutation of: + // - offset in [0, 4] + // - align in [1, 16] + // - elem_size in [0, 4] (plus no elem_size) + validate_against_rust!(0, 1); + validate_against_rust!(0, 1, 0); + validate_against_rust!(0, 1, 1); + validate_against_rust!(0, 1, 2); + validate_against_rust!(0, 1, 3); + validate_against_rust!(0, 1, 4); + validate_against_rust!(0, 2); + validate_against_rust!(0, 2, 0); + validate_against_rust!(0, 2, 1); + validate_against_rust!(0, 2, 2); + validate_against_rust!(0, 2, 3); + validate_against_rust!(0, 2, 4); + validate_against_rust!(0, 4); + validate_against_rust!(0, 4, 0); + validate_against_rust!(0, 4, 1); + validate_against_rust!(0, 4, 2); + validate_against_rust!(0, 4, 3); + validate_against_rust!(0, 4, 4); + validate_against_rust!(0, 8); + validate_against_rust!(0, 8, 0); + validate_against_rust!(0, 8, 1); + validate_against_rust!(0, 8, 2); + validate_against_rust!(0, 8, 3); + validate_against_rust!(0, 8, 4); + validate_against_rust!(0, 16); + validate_against_rust!(0, 16, 0); + validate_against_rust!(0, 16, 1); + validate_against_rust!(0, 16, 2); + validate_against_rust!(0, 16, 3); + validate_against_rust!(0, 16, 4); + validate_against_rust!(1, 1); + validate_against_rust!(1, 1, 0); + validate_against_rust!(1, 1, 1); + validate_against_rust!(1, 1, 2); + validate_against_rust!(1, 1, 3); + validate_against_rust!(1, 1, 4); + validate_against_rust!(1, 2); + validate_against_rust!(1, 2, 0); + validate_against_rust!(1, 2, 1); + validate_against_rust!(1, 2, 2); + validate_against_rust!(1, 2, 3); + validate_against_rust!(1, 2, 4); + validate_against_rust!(1, 4); + validate_against_rust!(1, 4, 0); + validate_against_rust!(1, 4, 1); + validate_against_rust!(1, 4, 2); + validate_against_rust!(1, 4, 3); + validate_against_rust!(1, 4, 4); + validate_against_rust!(1, 8); + validate_against_rust!(1, 8, 0); + validate_against_rust!(1, 8, 1); + validate_against_rust!(1, 8, 2); + validate_against_rust!(1, 8, 3); + validate_against_rust!(1, 8, 4); + validate_against_rust!(1, 16); + validate_against_rust!(1, 16, 0); + validate_against_rust!(1, 16, 1); + validate_against_rust!(1, 16, 2); + validate_against_rust!(1, 16, 3); + validate_against_rust!(1, 16, 4); + validate_against_rust!(2, 1); + validate_against_rust!(2, 1, 0); + validate_against_rust!(2, 1, 1); + validate_against_rust!(2, 1, 2); + validate_against_rust!(2, 1, 3); + validate_against_rust!(2, 1, 4); + validate_against_rust!(2, 2); + validate_against_rust!(2, 2, 0); + validate_against_rust!(2, 2, 1); + validate_against_rust!(2, 2, 2); + validate_against_rust!(2, 2, 3); + validate_against_rust!(2, 2, 4); + validate_against_rust!(2, 4); + validate_against_rust!(2, 4, 0); + validate_against_rust!(2, 4, 1); + validate_against_rust!(2, 4, 2); + validate_against_rust!(2, 4, 3); + validate_against_rust!(2, 4, 4); + validate_against_rust!(2, 8); + validate_against_rust!(2, 8, 0); + validate_against_rust!(2, 8, 1); + validate_against_rust!(2, 8, 2); + validate_against_rust!(2, 8, 3); + validate_against_rust!(2, 8, 4); + validate_against_rust!(2, 16); + validate_against_rust!(2, 16, 0); + validate_against_rust!(2, 16, 1); + validate_against_rust!(2, 16, 2); + validate_against_rust!(2, 16, 3); + validate_against_rust!(2, 16, 4); + validate_against_rust!(3, 1); + validate_against_rust!(3, 1, 0); + validate_against_rust!(3, 1, 1); + validate_against_rust!(3, 1, 2); + validate_against_rust!(3, 1, 3); + validate_against_rust!(3, 1, 4); + validate_against_rust!(3, 2); + validate_against_rust!(3, 2, 0); + validate_against_rust!(3, 2, 1); + validate_against_rust!(3, 2, 2); + validate_against_rust!(3, 2, 3); + validate_against_rust!(3, 2, 4); + validate_against_rust!(3, 4); + validate_against_rust!(3, 4, 0); + validate_against_rust!(3, 4, 1); + validate_against_rust!(3, 4, 2); + validate_against_rust!(3, 4, 3); + validate_against_rust!(3, 4, 4); + validate_against_rust!(3, 8); + validate_against_rust!(3, 8, 0); + validate_against_rust!(3, 8, 1); + validate_against_rust!(3, 8, 2); + validate_against_rust!(3, 8, 3); + validate_against_rust!(3, 8, 4); + validate_against_rust!(3, 16); + validate_against_rust!(3, 16, 0); + validate_against_rust!(3, 16, 1); + validate_against_rust!(3, 16, 2); + validate_against_rust!(3, 16, 3); + validate_against_rust!(3, 16, 4); + validate_against_rust!(4, 1); + validate_against_rust!(4, 1, 0); + validate_against_rust!(4, 1, 1); + validate_against_rust!(4, 1, 2); + validate_against_rust!(4, 1, 3); + validate_against_rust!(4, 1, 4); + validate_against_rust!(4, 2); + validate_against_rust!(4, 2, 0); + validate_against_rust!(4, 2, 1); + validate_against_rust!(4, 2, 2); + validate_against_rust!(4, 2, 3); + validate_against_rust!(4, 2, 4); + validate_against_rust!(4, 4); + validate_against_rust!(4, 4, 0); + validate_against_rust!(4, 4, 1); + validate_against_rust!(4, 4, 2); + validate_against_rust!(4, 4, 3); + validate_against_rust!(4, 4, 4); + validate_against_rust!(4, 8); + validate_against_rust!(4, 8, 0); + validate_against_rust!(4, 8, 1); + validate_against_rust!(4, 8, 2); + validate_against_rust!(4, 8, 3); + validate_against_rust!(4, 8, 4); + validate_against_rust!(4, 16); + validate_against_rust!(4, 16, 0); + validate_against_rust!(4, 16, 1); + validate_against_rust!(4, 16, 2); + validate_against_rust!(4, 16, 3); + validate_against_rust!(4, 16, 4); + } + + #[test] fn test_known_layout() { // Test that `$ty` and `ManuallyDrop<$ty>` have the expected layout. // Test that `PhantomData<$ty>` has the same layout as `()` regardless @@ -3191,9 +6264,15 @@ mod tests { }; } - let layout = |base_size, align, _trailing_slice_elem_size| DstLayout { - _base_layout: Layout::from_size_align(base_size, align).unwrap(), - _trailing_slice_elem_size, + let layout = |offset, align, _trailing_slice_elem_size| DstLayout { + align: NonZeroUsize::new(align).unwrap(), + size_info: match _trailing_slice_elem_size { + None => SizeInfo::Sized { _size: offset }, + Some(elem_size) => SizeInfo::SliceDst(TrailingSliceLayout { + _offset: offset, + _elem_size: elem_size, + }), + }, }; test!((), layout(0, 1, None)); @@ -3210,6 +6289,390 @@ mod tests { test!(str, layout(0, 1, Some(1))); } + #[cfg(feature = "derive")] + #[test] + fn test_known_layout_derive() { + // In this and other files (`late_compile_pass.rs`, + // `mid_compile_pass.rs`, and `struct.rs`), we test success and failure + // modes of `derive(KnownLayout)` for the following combination of + // properties: + // + // +------------+--------------------------------------+-----------+ + // | | trailing field properties | | + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // |------------+----------+----------------+----------+-----------| + // | N | N | N | N | KL00 | + // | N | N | N | Y | KL01 | + // | N | N | Y | N | KL02 | + // | N | N | Y | Y | KL03 | + // | N | Y | N | N | KL04 | + // | N | Y | N | Y | KL05 | + // | N | Y | Y | N | KL06 | + // | N | Y | Y | Y | KL07 | + // | Y | N | N | N | KL08 | + // | Y | N | N | Y | KL09 | + // | Y | N | Y | N | KL10 | + // | Y | N | Y | Y | KL11 | + // | Y | Y | N | N | KL12 | + // | Y | Y | N | Y | KL13 | + // | Y | Y | Y | N | KL14 | + // | Y | Y | Y | Y | KL15 | + // +------------+----------+----------------+----------+-----------+ + + struct NotKnownLayout<T = ()> { + _t: T, + } + + #[derive(KnownLayout)] + #[repr(C)] + struct AlignSize<const ALIGN: usize, const SIZE: usize> + where + elain::Align<ALIGN>: elain::Alignment, + { + _align: elain::Align<ALIGN>, + _size: [u8; SIZE], + } + + type AU16 = AlignSize<2, 2>; + type AU32 = AlignSize<4, 4>; + + fn _assert_kl<T: ?Sized + KnownLayout>(_: &T) {} + + let sized_layout = |align, size| DstLayout { + align: NonZeroUsize::new(align).unwrap(), + size_info: SizeInfo::Sized { _size: size }, + }; + + let unsized_layout = |align, elem_size, offset| DstLayout { + align: NonZeroUsize::new(align).unwrap(), + size_info: SizeInfo::SliceDst(TrailingSliceLayout { + _offset: offset, + _elem_size: elem_size, + }), + }; + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | N | N | N | Y | KL01 | + #[derive(KnownLayout)] + struct KL01(NotKnownLayout<AU32>, NotKnownLayout<AU16>); + + let expected = DstLayout::for_type::<KL01>(); + + assert_eq!(<KL01 as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL01 as KnownLayout>::LAYOUT, sized_layout(4, 8)); + + // ...with `align(N)`: + #[derive(KnownLayout)] + #[repr(align(64))] + struct KL01Align(NotKnownLayout<AU32>, NotKnownLayout<AU16>); + + let expected = DstLayout::for_type::<KL01Align>(); + + assert_eq!(<KL01Align as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL01Align as KnownLayout>::LAYOUT, sized_layout(64, 64)); + + // ...with `packed`: + #[derive(KnownLayout)] + #[repr(packed)] + struct KL01Packed(NotKnownLayout<AU32>, NotKnownLayout<AU16>); + + let expected = DstLayout::for_type::<KL01Packed>(); + + assert_eq!(<KL01Packed as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL01Packed as KnownLayout>::LAYOUT, sized_layout(1, 6)); + + // ...with `packed(N)`: + #[derive(KnownLayout)] + #[repr(packed(2))] + struct KL01PackedN(NotKnownLayout<AU32>, NotKnownLayout<AU16>); + + assert_impl_all!(KL01PackedN: KnownLayout); + + let expected = DstLayout::for_type::<KL01PackedN>(); + + assert_eq!(<KL01PackedN as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL01PackedN as KnownLayout>::LAYOUT, sized_layout(2, 6)); + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | N | N | Y | Y | KL03 | + #[derive(KnownLayout)] + struct KL03(NotKnownLayout, u8); + + let expected = DstLayout::for_type::<KL03>(); + + assert_eq!(<KL03 as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL03 as KnownLayout>::LAYOUT, sized_layout(1, 1)); + + // ... with `align(N)` + #[derive(KnownLayout)] + #[repr(align(64))] + struct KL03Align(NotKnownLayout<AU32>, u8); + + let expected = DstLayout::for_type::<KL03Align>(); + + assert_eq!(<KL03Align as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL03Align as KnownLayout>::LAYOUT, sized_layout(64, 64)); + + // ... with `packed`: + #[derive(KnownLayout)] + #[repr(packed)] + struct KL03Packed(NotKnownLayout<AU32>, u8); + + let expected = DstLayout::for_type::<KL03Packed>(); + + assert_eq!(<KL03Packed as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL03Packed as KnownLayout>::LAYOUT, sized_layout(1, 5)); + + // ... with `packed(N)` + #[derive(KnownLayout)] + #[repr(packed(2))] + struct KL03PackedN(NotKnownLayout<AU32>, u8); + + assert_impl_all!(KL03PackedN: KnownLayout); + + let expected = DstLayout::for_type::<KL03PackedN>(); + + assert_eq!(<KL03PackedN as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL03PackedN as KnownLayout>::LAYOUT, sized_layout(2, 6)); + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | N | Y | N | Y | KL05 | + #[derive(KnownLayout)] + struct KL05<T>(u8, T); + + fn _test_kl05<T>(t: T) -> impl KnownLayout { + KL05(0u8, t) + } + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | N | Y | Y | Y | KL07 | + #[derive(KnownLayout)] + struct KL07<T: KnownLayout>(u8, T); + + fn _test_kl07<T: KnownLayout>(t: T) -> impl KnownLayout { + let _ = KL07(0u8, t); + } + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | Y | N | Y | N | KL10 | + #[derive(KnownLayout)] + #[repr(C)] + struct KL10(NotKnownLayout<AU32>, [u8]); + + let expected = DstLayout::new_zst(None) + .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), None) + .extend(<[u8] as KnownLayout>::LAYOUT, None) + .pad_to_align(); + + assert_eq!(<KL10 as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL10 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 4)); + + // ...with `align(N)`: + #[derive(KnownLayout)] + #[repr(C, align(64))] + struct KL10Align(NotKnownLayout<AU32>, [u8]); + + let repr_align = NonZeroUsize::new(64); + + let expected = DstLayout::new_zst(repr_align) + .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), None) + .extend(<[u8] as KnownLayout>::LAYOUT, None) + .pad_to_align(); + + assert_eq!(<KL10Align as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL10Align as KnownLayout>::LAYOUT, unsized_layout(64, 1, 4)); + + // ...with `packed`: + #[derive(KnownLayout)] + #[repr(C, packed)] + struct KL10Packed(NotKnownLayout<AU32>, [u8]); + + let repr_packed = NonZeroUsize::new(1); + + let expected = DstLayout::new_zst(None) + .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), repr_packed) + .extend(<[u8] as KnownLayout>::LAYOUT, repr_packed) + .pad_to_align(); + + assert_eq!(<KL10Packed as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL10Packed as KnownLayout>::LAYOUT, unsized_layout(1, 1, 4)); + + // ...with `packed(N)`: + #[derive(KnownLayout)] + #[repr(C, packed(2))] + struct KL10PackedN(NotKnownLayout<AU32>, [u8]); + + let repr_packed = NonZeroUsize::new(2); + + let expected = DstLayout::new_zst(None) + .extend(DstLayout::for_type::<NotKnownLayout<AU32>>(), repr_packed) + .extend(<[u8] as KnownLayout>::LAYOUT, repr_packed) + .pad_to_align(); + + assert_eq!(<KL10PackedN as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL10PackedN as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4)); + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | Y | N | Y | Y | KL11 | + #[derive(KnownLayout)] + #[repr(C)] + struct KL11(NotKnownLayout<AU64>, u8); + + let expected = DstLayout::new_zst(None) + .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), None) + .extend(<u8 as KnownLayout>::LAYOUT, None) + .pad_to_align(); + + assert_eq!(<KL11 as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL11 as KnownLayout>::LAYOUT, sized_layout(8, 16)); + + // ...with `align(N)`: + #[derive(KnownLayout)] + #[repr(C, align(64))] + struct KL11Align(NotKnownLayout<AU64>, u8); + + let repr_align = NonZeroUsize::new(64); + + let expected = DstLayout::new_zst(repr_align) + .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), None) + .extend(<u8 as KnownLayout>::LAYOUT, None) + .pad_to_align(); + + assert_eq!(<KL11Align as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL11Align as KnownLayout>::LAYOUT, sized_layout(64, 64)); + + // ...with `packed`: + #[derive(KnownLayout)] + #[repr(C, packed)] + struct KL11Packed(NotKnownLayout<AU64>, u8); + + let repr_packed = NonZeroUsize::new(1); + + let expected = DstLayout::new_zst(None) + .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), repr_packed) + .extend(<u8 as KnownLayout>::LAYOUT, repr_packed) + .pad_to_align(); + + assert_eq!(<KL11Packed as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL11Packed as KnownLayout>::LAYOUT, sized_layout(1, 9)); + + // ...with `packed(N)`: + #[derive(KnownLayout)] + #[repr(C, packed(2))] + struct KL11PackedN(NotKnownLayout<AU64>, u8); + + let repr_packed = NonZeroUsize::new(2); + + let expected = DstLayout::new_zst(None) + .extend(DstLayout::for_type::<NotKnownLayout<AU64>>(), repr_packed) + .extend(<u8 as KnownLayout>::LAYOUT, repr_packed) + .pad_to_align(); + + assert_eq!(<KL11PackedN as KnownLayout>::LAYOUT, expected); + assert_eq!(<KL11PackedN as KnownLayout>::LAYOUT, sized_layout(2, 10)); + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | Y | Y | Y | N | KL14 | + #[derive(KnownLayout)] + #[repr(C)] + struct KL14<T: ?Sized + KnownLayout>(u8, T); + + fn _test_kl14<T: ?Sized + KnownLayout>(kl: &KL14<T>) { + _assert_kl(kl) + } + + // | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name | + // | Y | Y | Y | Y | KL15 | + #[derive(KnownLayout)] + #[repr(C)] + struct KL15<T: KnownLayout>(u8, T); + + fn _test_kl15<T: KnownLayout>(t: T) -> impl KnownLayout { + let _ = KL15(0u8, t); + } + + // Test a variety of combinations of field types: + // - () + // - u8 + // - AU16 + // - [()] + // - [u8] + // - [AU16] + + #[allow(clippy::upper_case_acronyms)] + #[derive(KnownLayout)] + #[repr(C)] + struct KLTU<T, U: ?Sized>(T, U); + + assert_eq!(<KLTU<(), ()> as KnownLayout>::LAYOUT, sized_layout(1, 0)); + + assert_eq!(<KLTU<(), u8> as KnownLayout>::LAYOUT, sized_layout(1, 1)); + + assert_eq!(<KLTU<(), AU16> as KnownLayout>::LAYOUT, sized_layout(2, 2)); + + assert_eq!(<KLTU<(), [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 0)); + + assert_eq!(<KLTU<(), [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0)); + + assert_eq!(<KLTU<(), [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 0)); + + assert_eq!(<KLTU<u8, ()> as KnownLayout>::LAYOUT, sized_layout(1, 1)); + + assert_eq!(<KLTU<u8, u8> as KnownLayout>::LAYOUT, sized_layout(1, 2)); + + assert_eq!(<KLTU<u8, AU16> as KnownLayout>::LAYOUT, sized_layout(2, 4)); + + assert_eq!(<KLTU<u8, [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 1)); + + assert_eq!(<KLTU<u8, [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1)); + + assert_eq!(<KLTU<u8, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2)); + + assert_eq!(<KLTU<AU16, ()> as KnownLayout>::LAYOUT, sized_layout(2, 2)); + + assert_eq!(<KLTU<AU16, u8> as KnownLayout>::LAYOUT, sized_layout(2, 4)); + + assert_eq!(<KLTU<AU16, AU16> as KnownLayout>::LAYOUT, sized_layout(2, 4)); + + assert_eq!(<KLTU<AU16, [()]> as KnownLayout>::LAYOUT, unsized_layout(2, 0, 2)); + + assert_eq!(<KLTU<AU16, [u8]> as KnownLayout>::LAYOUT, unsized_layout(2, 1, 2)); + + assert_eq!(<KLTU<AU16, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2)); + + // Test a variety of field counts. + + #[derive(KnownLayout)] + #[repr(C)] + struct KLF0; + + assert_eq!(<KLF0 as KnownLayout>::LAYOUT, sized_layout(1, 0)); + + #[derive(KnownLayout)] + #[repr(C)] + struct KLF1([u8]); + + assert_eq!(<KLF1 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0)); + + #[derive(KnownLayout)] + #[repr(C)] + struct KLF2(NotKnownLayout<u8>, [u8]); + + assert_eq!(<KLF2 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1)); + + #[derive(KnownLayout)] + #[repr(C)] + struct KLF3(NotKnownLayout<u8>, NotKnownLayout<AU16>, [u8]); + + assert_eq!(<KLF3 as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4)); + + #[derive(KnownLayout)] + #[repr(C)] + struct KLF4(NotKnownLayout<u8>, NotKnownLayout<AU16>, NotKnownLayout<AU32>, [u8]); + + assert_eq!(<KLF4 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 8)); + } + #[test] fn test_object_safety() { fn _takes_from_zeroes(_: &dyn FromZeroes) {} @@ -3300,6 +6763,7 @@ mod tests { panic!("PanicOnDrop::drop"); } } + #[allow(clippy::let_unit_value)] let _: () = transmute!(PanicOnDrop(())); // Test that `transmute!` is legal in a const context. @@ -3310,6 +6774,95 @@ mod tests { } #[test] + fn test_transmute_ref() { + // Test that memory is transmuted as expected. + let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7]; + let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]]; + let x: &[[u8; 2]; 4] = transmute_ref!(&array_of_u8s); + assert_eq!(*x, array_of_arrays); + let x: &[u8; 8] = transmute_ref!(&array_of_arrays); + assert_eq!(*x, array_of_u8s); + + // Test that `transmute_ref!` is legal in a const context. + const ARRAY_OF_U8S: [u8; 8] = [0u8, 1, 2, 3, 4, 5, 6, 7]; + const ARRAY_OF_ARRAYS: [[u8; 2]; 4] = [[0, 1], [2, 3], [4, 5], [6, 7]]; + #[allow(clippy::redundant_static_lifetimes)] + const X: &'static [[u8; 2]; 4] = transmute_ref!(&ARRAY_OF_U8S); + assert_eq!(*X, ARRAY_OF_ARRAYS); + + // Test that it's legal to transmute a reference while shrinking the + // lifetime (note that `X` has the lifetime `'static`). + let x: &[u8; 8] = transmute_ref!(X); + assert_eq!(*x, ARRAY_OF_U8S); + + // Test that `transmute_ref!` supports decreasing alignment. + let u = AU64(0); + let array = [0, 0, 0, 0, 0, 0, 0, 0]; + let x: &[u8; 8] = transmute_ref!(&u); + assert_eq!(*x, array); + + // Test that a mutable reference can be turned into an immutable one. + let mut x = 0u8; + #[allow(clippy::useless_transmute)] + let y: &u8 = transmute_ref!(&mut x); + assert_eq!(*y, 0); + } + + #[test] + fn test_transmute_mut() { + // Test that memory is transmuted as expected. + let mut array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7]; + let mut array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]]; + let x: &mut [[u8; 2]; 4] = transmute_mut!(&mut array_of_u8s); + assert_eq!(*x, array_of_arrays); + let x: &mut [u8; 8] = transmute_mut!(&mut array_of_arrays); + assert_eq!(*x, array_of_u8s); + + { + // Test that it's legal to transmute a reference while shrinking the + // lifetime. + let x: &mut [u8; 8] = transmute_mut!(&mut array_of_arrays); + assert_eq!(*x, array_of_u8s); + } + // Test that `transmute_mut!` supports decreasing alignment. + let mut u = AU64(0); + let array = [0, 0, 0, 0, 0, 0, 0, 0]; + let x: &[u8; 8] = transmute_mut!(&mut u); + assert_eq!(*x, array); + + // Test that a mutable reference can be turned into an immutable one. + let mut x = 0u8; + #[allow(clippy::useless_transmute)] + let y: &u8 = transmute_mut!(&mut x); + assert_eq!(*y, 0); + } + + #[test] + fn test_macros_evaluate_args_once() { + let mut ctr = 0; + let _: usize = transmute!({ + ctr += 1; + 0usize + }); + assert_eq!(ctr, 1); + + let mut ctr = 0; + let _: &usize = transmute_ref!({ + ctr += 1; + &0usize + }); + assert_eq!(ctr, 1); + } + + #[test] + fn test_include_value() { + const AS_U32: u32 = include_value!("../testdata/include_value/data"); + assert_eq!(AS_U32, u32::from_ne_bytes([b'a', b'b', b'c', b'd'])); + const AS_I32: i32 = include_value!("../testdata/include_value/data"); + assert_eq!(AS_I32, i32::from_ne_bytes([b'a', b'b', b'c', b'd'])); + } + + #[test] fn test_address() { // Test that the `Deref` and `DerefMut` implementations return a // reference which points to the right region of memory. @@ -3445,7 +6998,8 @@ mod tests { let mut buf = Align::<[u8; 8], AU64>::default(); // `buf.t` should be aligned to 8, so this should always succeed. test_new_helper(Ref::<_, AU64>::new(&mut buf.t[..]).unwrap()); - buf.t = [0xFFu8; 8]; + let ascending: [u8; 8] = (0..8).collect::<Vec<_>>().try_into().unwrap(); + buf.t = ascending; test_new_helper(Ref::<_, AU64>::new_zeroed(&mut buf.t[..]).unwrap()); { // In a block so that `r` and `suffix` don't live too long. @@ -3455,7 +7009,7 @@ mod tests { test_new_helper(r); } { - buf.t = [0xFFu8; 8]; + buf.t = ascending; let (r, suffix) = Ref::<_, AU64>::new_from_prefix_zeroed(&mut buf.t[..]).unwrap(); assert!(suffix.is_empty()); test_new_helper(r); @@ -3467,44 +7021,54 @@ mod tests { test_new_helper(r); } { - buf.t = [0xFFu8; 8]; + buf.t = ascending; let (prefix, r) = Ref::<_, AU64>::new_from_suffix_zeroed(&mut buf.t[..]).unwrap(); assert!(prefix.is_empty()); test_new_helper(r); } - // A buffer with alignment 8 and length 16. - let mut buf = Align::<[u8; 16], AU64>::default(); + // A buffer with alignment 8 and length 24. We choose this length very + // intentionally: if we instead used length 16, then the prefix and + // suffix lengths would be identical. In the past, we used length 16, + // which resulted in this test failing to discover the bug uncovered in + // #506. + let mut buf = Align::<[u8; 24], AU64>::default(); // `buf.t` should be aligned to 8 and have a length which is a multiple // of `size_of::<AU64>()`, so this should always succeed. - test_new_helper_slice(Ref::<_, [AU64]>::new_slice(&mut buf.t[..]).unwrap(), 2); - buf.t = [0xFFu8; 16]; - test_new_helper_slice(Ref::<_, [AU64]>::new_slice_zeroed(&mut buf.t[..]).unwrap(), 2); + test_new_helper_slice(Ref::<_, [AU64]>::new_slice(&mut buf.t[..]).unwrap(), 3); + let ascending: [u8; 24] = (0..24).collect::<Vec<_>>().try_into().unwrap(); + // 16 ascending bytes followed by 8 zeros. + let mut ascending_prefix = ascending; + ascending_prefix[16..].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); + // 8 zeros followed by 16 ascending bytes. + let mut ascending_suffix = ascending; + ascending_suffix[..8].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); + test_new_helper_slice(Ref::<_, [AU64]>::new_slice_zeroed(&mut buf.t[..]).unwrap(), 3); { - buf.set_default(); + buf.t = ascending_suffix; let (r, suffix) = Ref::<_, [AU64]>::new_slice_from_prefix(&mut buf.t[..], 1).unwrap(); - assert_eq!(suffix, [0; 8]); + assert_eq!(suffix, &ascending[8..]); test_new_helper_slice(r, 1); } { - buf.t = [0xFFu8; 16]; + buf.t = ascending_suffix; let (r, suffix) = Ref::<_, [AU64]>::new_slice_from_prefix_zeroed(&mut buf.t[..], 1).unwrap(); - assert_eq!(suffix, [0xFF; 8]); + assert_eq!(suffix, &ascending[8..]); test_new_helper_slice(r, 1); } { - buf.set_default(); + buf.t = ascending_prefix; let (prefix, r) = Ref::<_, [AU64]>::new_slice_from_suffix(&mut buf.t[..], 1).unwrap(); - assert_eq!(prefix, [0; 8]); + assert_eq!(prefix, &ascending[..16]); test_new_helper_slice(r, 1); } { - buf.t = [0xFFu8; 16]; + buf.t = ascending_prefix; let (prefix, r) = Ref::<_, [AU64]>::new_slice_from_suffix_zeroed(&mut buf.t[..], 1).unwrap(); - assert_eq!(prefix, [0xFF; 8]); + assert_eq!(prefix, &ascending[..16]); test_new_helper_slice(r, 1); } } @@ -3673,6 +7237,71 @@ mod tests { } #[test] + fn test_ref_from_mut_from() { + // Test `FromBytes::{ref_from, mut_from}{,_prefix,_suffix}` success cases + // Exhaustive coverage for these methods is covered by the `Ref` tests above, + // which these helper methods defer to. + + let mut buf = + Align::<[u8; 16], AU64>::new([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + + assert_eq!( + AU64::ref_from(&buf.t[8..]).unwrap().0.to_ne_bytes(), + [8, 9, 10, 11, 12, 13, 14, 15] + ); + let suffix = AU64::mut_from(&mut buf.t[8..]).unwrap(); + suffix.0 = 0x0101010101010101; + // The `[u8:9]` is a non-half size of the full buffer, which would catch + // `from_prefix` having the same implementation as `from_suffix` (issues #506, #511). + assert_eq!(<[u8; 9]>::ref_from_suffix(&buf.t[..]).unwrap(), &[7u8, 1, 1, 1, 1, 1, 1, 1, 1]); + let suffix = AU64::mut_from_suffix(&mut buf.t[1..]).unwrap(); + suffix.0 = 0x0202020202020202; + <[u8; 10]>::mut_from_suffix(&mut buf.t[..]).unwrap()[0] = 42; + assert_eq!(<[u8; 9]>::ref_from_prefix(&buf.t[..]).unwrap(), &[0, 1, 2, 3, 4, 5, 42, 7, 2]); + <[u8; 2]>::mut_from_prefix(&mut buf.t[..]).unwrap()[1] = 30; + assert_eq!(buf.t, [0, 30, 2, 3, 4, 5, 42, 7, 2, 2, 2, 2, 2, 2, 2, 2]); + } + + #[test] + fn test_ref_from_mut_from_error() { + // Test `FromBytes::{ref_from, mut_from}{,_prefix,_suffix}` error cases. + + // Fail because the buffer is too large. + let mut buf = Align::<[u8; 16], AU64>::default(); + // `buf.t` should be aligned to 8, so only the length check should fail. + assert!(AU64::ref_from(&buf.t[..]).is_none()); + assert!(AU64::mut_from(&mut buf.t[..]).is_none()); + assert!(<[u8; 8]>::ref_from(&buf.t[..]).is_none()); + assert!(<[u8; 8]>::mut_from(&mut buf.t[..]).is_none()); + + // Fail because the buffer is too small. + let mut buf = Align::<[u8; 4], AU64>::default(); + assert!(AU64::ref_from(&buf.t[..]).is_none()); + assert!(AU64::mut_from(&mut buf.t[..]).is_none()); + assert!(<[u8; 8]>::ref_from(&buf.t[..]).is_none()); + assert!(<[u8; 8]>::mut_from(&mut buf.t[..]).is_none()); + assert!(AU64::ref_from_prefix(&buf.t[..]).is_none()); + assert!(AU64::mut_from_prefix(&mut buf.t[..]).is_none()); + assert!(AU64::ref_from_suffix(&buf.t[..]).is_none()); + assert!(AU64::mut_from_suffix(&mut buf.t[..]).is_none()); + assert!(<[u8; 8]>::ref_from_prefix(&buf.t[..]).is_none()); + assert!(<[u8; 8]>::mut_from_prefix(&mut buf.t[..]).is_none()); + assert!(<[u8; 8]>::ref_from_suffix(&buf.t[..]).is_none()); + assert!(<[u8; 8]>::mut_from_suffix(&mut buf.t[..]).is_none()); + + // Fail because the alignment is insufficient. + let mut buf = Align::<[u8; 13], AU64>::default(); + assert!(AU64::ref_from(&buf.t[1..]).is_none()); + assert!(AU64::mut_from(&mut buf.t[1..]).is_none()); + assert!(AU64::ref_from(&buf.t[1..]).is_none()); + assert!(AU64::mut_from(&mut buf.t[1..]).is_none()); + assert!(AU64::ref_from_prefix(&buf.t[1..]).is_none()); + assert!(AU64::mut_from_prefix(&mut buf.t[1..]).is_none()); + assert!(AU64::ref_from_suffix(&buf.t[..]).is_none()); + assert!(AU64::mut_from_suffix(&mut buf.t[..]).is_none()); + } + + #[test] #[allow(clippy::cognitive_complexity)] fn test_new_error() { // Fail because the buffer is too large. @@ -3988,9 +7617,152 @@ mod tests { #[test] fn test_impls() { + use core::borrow::Borrow; + + // A type that can supply test cases for testing + // `TryFromBytes::is_bit_valid`. All types passed to `assert_impls!` + // must implement this trait; that macro uses it to generate runtime + // tests for `TryFromBytes` impls. + // + // All `T: FromBytes` types are provided with a blanket impl. Other + // types must implement `TryFromBytesTestable` directly (ie using + // `impl_try_from_bytes_testable!`). + trait TryFromBytesTestable { + fn with_passing_test_cases<F: Fn(&Self)>(f: F); + fn with_failing_test_cases<F: Fn(&[u8])>(f: F); + } + + impl<T: FromBytes> TryFromBytesTestable for T { + fn with_passing_test_cases<F: Fn(&Self)>(f: F) { + // Test with a zeroed value. + f(&Self::new_zeroed()); + + let ffs = { + let mut t = Self::new_zeroed(); + let ptr: *mut T = &mut t; + // SAFETY: `T: FromBytes` + unsafe { ptr::write_bytes(ptr.cast::<u8>(), 0xFF, mem::size_of::<T>()) }; + t + }; + + // Test with a value initialized with 0xFF. + f(&ffs); + } + + fn with_failing_test_cases<F: Fn(&[u8])>(_f: F) {} + } + + // Implements `TryFromBytesTestable`. + macro_rules! impl_try_from_bytes_testable { + // Base case for recursion (when the list of types has run out). + (=> @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => {}; + // Implements for type(s) with no type parameters. + ($ty:ty $(,$tys:ty)* => @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => { + impl TryFromBytesTestable for $ty { + impl_try_from_bytes_testable!( + @methods @success $($success_case),* + $(, @failure $($failure_case),*)? + ); + } + impl_try_from_bytes_testable!($($tys),* => @success $($success_case),* $(, @failure $($failure_case),*)?); + }; + // Implements for multiple types with no type parameters. + ($($($ty:ty),* => @success $($success_case:expr), * $(, @failure $($failure_case:expr),*)?;)*) => { + $( + impl_try_from_bytes_testable!($($ty),* => @success $($success_case),* $(, @failure $($failure_case),*)*); + )* + }; + // Implements only the methods; caller must invoke this from inside + // an impl block. + (@methods @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => { + fn with_passing_test_cases<F: Fn(&Self)>(_f: F) { + $( + _f($success_case.borrow()); + )* + } + + fn with_failing_test_cases<F: Fn(&[u8])>(_f: F) { + $($( + // `unused_qualifications` is spuriously triggered on + // `Option::<Self>::None`. + #[allow(unused_qualifications)] + let case = $failure_case.as_bytes(); + _f(case.as_bytes()); + )*)? + } + }; + } + + // Note that these impls are only for types which are not `FromBytes`. + // `FromBytes` types are covered by a preceding blanket impl. + impl_try_from_bytes_testable!( + bool => @success true, false, + @failure 2u8, 3u8, 0xFFu8; + char => @success '\u{0}', '\u{D7FF}', '\u{E000}', '\u{10FFFF}', + @failure 0xD800u32, 0xDFFFu32, 0x110000u32; + str => @success "", "hello", "❤️🧡💛💚💙💜", + @failure [0, 159, 146, 150]; + [u8] => @success [], [0, 1, 2]; + NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, + NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, + NonZeroUsize, NonZeroIsize + => @success Self::new(1).unwrap(), + // Doing this instead of `0` ensures that we always satisfy + // the size and alignment requirements of `Self` (whereas + // `0` may be any integer type with a different size or + // alignment than some `NonZeroXxx` types). + @failure Option::<Self>::None; + [bool] + => @success [true, false], [false, true], + @failure [2u8], [3u8], [0xFFu8], [0u8, 1u8, 2u8]; + ); + // Asserts that `$ty` implements any `$trait` and doesn't implement any // `!$trait`. Note that all `$trait`s must come before any `!$trait`s. + // + // For `T: TryFromBytes`, uses `TryFromBytesTestable` to test success + // and failure cases for `TryFromBytes::is_bit_valid`. macro_rules! assert_impls { + ($ty:ty: TryFromBytes) => { + <$ty as TryFromBytesTestable>::with_passing_test_cases(|val| { + let c = Ptr::from(val); + // SAFETY: + // - Since `val` is a normal reference, `c` is guranteed to + // be aligned, to point to a single allocation, and to + // have a size which doesn't overflow `isize`. + // - Since `val` is a valid `$ty`, `c`'s referent satisfies + // the bit validity constraints of `is_bit_valid`, which + // are a superset of the bit validity constraints of + // `$ty`. + let res = unsafe { <$ty as TryFromBytes>::is_bit_valid(c) }; + assert!(res, "{}::is_bit_valid({:?}): got false, expected true", stringify!($ty), val); + + // TODO(#5): In addition to testing `is_bit_valid`, test the + // methods built on top of it. This would both allow us to + // test their implementations and actually convert the bytes + // to `$ty`, giving Miri a chance to catch if this is + // unsound (ie, if our `is_bit_valid` impl is buggy). + // + // The following code was tried, but it doesn't work because + // a) some types are not `AsBytes` and, b) some types are + // not `Sized`. + // + // let r = <$ty as TryFromBytes>::try_from_ref(val.as_bytes()).unwrap(); + // assert_eq!(r, &val); + // let r = <$ty as TryFromBytes>::try_from_mut(val.as_bytes_mut()).unwrap(); + // assert_eq!(r, &mut val); + // let v = <$ty as TryFromBytes>::try_read_from(val.as_bytes()).unwrap(); + // assert_eq!(v, val); + }); + #[allow(clippy::as_conversions)] + <$ty as TryFromBytesTestable>::with_failing_test_cases(|c| { + let res = <$ty as TryFromBytes>::try_from_ref(c); + assert!(res.is_none(), "{}::is_bit_valid({:?}): got true, expected false", stringify!($ty), c); + }); + + #[allow(dead_code)] + const _: () = { static_assertions::assert_impl_all!($ty: TryFromBytes); }; + }; ($ty:ty: $trait:ident) => { #[allow(dead_code)] const _: () = { static_assertions::assert_impl_all!($ty: $trait); }; @@ -4010,77 +7782,416 @@ mod tests { }; } - assert_impls!((): FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(u8: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(i8: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(u16: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(i16: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(u32: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(i32: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(u64: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(i64: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(u128: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(i128: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(usize: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(isize: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(f32: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(f64: FromZeroes, FromBytes, AsBytes, !Unaligned); - - assert_impls!(bool: FromZeroes, AsBytes, Unaligned, !FromBytes); - assert_impls!(char: FromZeroes, AsBytes, !FromBytes, !Unaligned); - assert_impls!(str: FromZeroes, AsBytes, Unaligned, !FromBytes); - - assert_impls!(NonZeroU8: AsBytes, Unaligned, !FromZeroes, !FromBytes); - assert_impls!(NonZeroI8: AsBytes, Unaligned, !FromZeroes, !FromBytes); - assert_impls!(NonZeroU16: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroI16: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroU32: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroI32: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroU64: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroI64: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroU128: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroI128: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroUsize: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - assert_impls!(NonZeroIsize: AsBytes, !FromZeroes, !FromBytes, !Unaligned); - - assert_impls!(Option<NonZeroU8>: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(Option<NonZeroI8>: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(Option<NonZeroU16>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroI16>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroU32>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroI32>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroU64>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroI64>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroU128>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroI128>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroUsize>: FromZeroes, FromBytes, AsBytes, !Unaligned); - assert_impls!(Option<NonZeroIsize>: FromZeroes, FromBytes, AsBytes, !Unaligned); + // NOTE: The negative impl assertions here are not necessarily + // prescriptive. They merely serve as change detectors to make sure + // we're aware of what trait impls are getting added with a given + // change. Of course, some impls would be invalid (e.g., `bool: + // FromBytes`), and so this change detection is very important. + + assert_impls!((): KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!(u8: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!(i8: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!(u16: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(i16: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(u32: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(i32: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(u64: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(i64: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(u128: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(i128: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(usize: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(isize: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(f32: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(f64: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + + assert_impls!(bool: KnownLayout, TryFromBytes, FromZeroes, AsBytes, Unaligned, !FromBytes); + assert_impls!(char: KnownLayout, TryFromBytes, FromZeroes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(str: KnownLayout, TryFromBytes, FromZeroes, AsBytes, Unaligned, !FromBytes); + + assert_impls!(NonZeroU8: KnownLayout, TryFromBytes, AsBytes, Unaligned, !FromZeroes, !FromBytes); + assert_impls!(NonZeroI8: KnownLayout, TryFromBytes, AsBytes, Unaligned, !FromZeroes, !FromBytes); + assert_impls!(NonZeroU16: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroI16: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroU32: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroI32: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroU64: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroI64: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroU128: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroI128: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroUsize: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + assert_impls!(NonZeroIsize: KnownLayout, TryFromBytes, AsBytes, !FromBytes, !Unaligned); + + assert_impls!(Option<NonZeroU8>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!(Option<NonZeroI8>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!(Option<NonZeroU16>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroI16>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroU32>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroI32>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroU64>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroI64>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroU128>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroI128>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroUsize>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); + assert_impls!(Option<NonZeroIsize>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); // Implements none of the ZC traits. struct NotZerocopy; - assert_impls!(PhantomData<NotZerocopy>: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(PhantomData<[u8]>: FromZeroes, FromBytes, AsBytes, Unaligned); + #[rustfmt::skip] + type FnManyArgs = fn( + NotZerocopy, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, + ) -> (NotZerocopy, NotZerocopy); + + // Allowed, because we're not actually using this type for FFI. + #[allow(improper_ctypes_definitions)] + #[rustfmt::skip] + type ECFnManyArgs = extern "C" fn( + NotZerocopy, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, + ) -> (NotZerocopy, NotZerocopy); + + #[cfg(feature = "alloc")] + assert_impls!(Option<Box<UnsafeCell<NotZerocopy>>>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<Box<[UnsafeCell<NotZerocopy>]>>: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<&'static UnsafeCell<NotZerocopy>>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<&'static [UnsafeCell<NotZerocopy>]>: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<&'static mut UnsafeCell<NotZerocopy>>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<&'static mut [UnsafeCell<NotZerocopy>]>: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<NonNull<UnsafeCell<NotZerocopy>>>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<NonNull<[UnsafeCell<NotZerocopy>]>>: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<fn()>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<FnManyArgs>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<extern "C" fn()>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(Option<ECFnManyArgs>: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + + assert_impls!(PhantomData<NotZerocopy>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!(PhantomData<[u8]>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + + assert_impls!(ManuallyDrop<u8>: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); + assert_impls!(ManuallyDrop<[u8]>: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); + assert_impls!(ManuallyDrop<NotZerocopy>: !TryFromBytes, !KnownLayout, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(ManuallyDrop<[NotZerocopy]>: !TryFromBytes, !KnownLayout, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + + assert_impls!(MaybeUninit<u8>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, Unaligned, !AsBytes); + assert_impls!(MaybeUninit<NotZerocopy>: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + + assert_impls!(Wrapping<u8>: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!(Wrapping<NotZerocopy>: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + + assert_impls!(Unalign<u8>: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); + assert_impls!(Unalign<NotZerocopy>: Unaligned, !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes); + + assert_impls!([u8]: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!([bool]: KnownLayout, TryFromBytes, FromZeroes, AsBytes, Unaligned, !FromBytes); + assert_impls!([NotZerocopy]: !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!([u8; 0]: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); + assert_impls!([NotZerocopy; 0]: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!([u8; 1]: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); + assert_impls!([NotZerocopy; 1]: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + + assert_impls!(*const NotZerocopy: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(*mut NotZerocopy: KnownLayout, FromZeroes, !TryFromBytes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(*const [NotZerocopy]: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(*mut [NotZerocopy]: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(*const dyn Debug: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(*mut dyn Debug: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + + #[cfg(feature = "simd")] + { + #[allow(unused_macros)] + macro_rules! test_simd_arch_mod { + ($arch:ident, $($typ:ident),*) => { + { + use core::arch::$arch::{$($typ),*}; + use crate::*; + $( assert_impls!($typ: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, !Unaligned); )* + } + }; + } + #[cfg(target_arch = "x86")] + test_simd_arch_mod!(x86, __m128, __m128d, __m128i, __m256, __m256d, __m256i); + + #[cfg(all(feature = "simd-nightly", target_arch = "x86"))] + test_simd_arch_mod!(x86, __m512bh, __m512, __m512d, __m512i); + + #[cfg(target_arch = "x86_64")] + test_simd_arch_mod!(x86_64, __m128, __m128d, __m128i, __m256, __m256d, __m256i); + + #[cfg(all(feature = "simd-nightly", target_arch = "x86_64"))] + test_simd_arch_mod!(x86_64, __m512bh, __m512, __m512d, __m512i); + + #[cfg(target_arch = "wasm32")] + test_simd_arch_mod!(wasm32, v128); + + #[cfg(all(feature = "simd-nightly", target_arch = "powerpc"))] + test_simd_arch_mod!( + powerpc, + vector_bool_long, + vector_double, + vector_signed_long, + vector_unsigned_long + ); + + #[cfg(all(feature = "simd-nightly", target_arch = "powerpc64"))] + test_simd_arch_mod!( + powerpc64, + vector_bool_long, + vector_double, + vector_signed_long, + vector_unsigned_long + ); + #[cfg(target_arch = "aarch64")] + #[rustfmt::skip] + test_simd_arch_mod!( + aarch64, float32x2_t, float32x4_t, float64x1_t, float64x2_t, int8x8_t, int8x8x2_t, + int8x8x3_t, int8x8x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int16x4_t, + int16x8_t, int32x2_t, int32x4_t, int64x1_t, int64x2_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, + poly8x8x4_t, poly8x16_t, poly8x16x2_t, poly8x16x3_t, poly8x16x4_t, poly16x4_t, poly16x8_t, + poly64x1_t, poly64x2_t, uint8x8_t, uint8x8x2_t, uint8x8x3_t, uint8x8x4_t, uint8x16_t, + uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint16x4_t, uint16x8_t, uint32x2_t, uint32x4_t, + uint64x1_t, uint64x2_t + ); + #[cfg(all(feature = "simd-nightly", target_arch = "arm"))] + #[rustfmt::skip] + test_simd_arch_mod!(arm, int8x4_t, uint8x4_t); + } + } +} + +#[cfg(kani)] +mod proofs { + use super::*; + + impl kani::Arbitrary for DstLayout { + fn any() -> Self { + let align: NonZeroUsize = kani::any(); + let size_info: SizeInfo = kani::any(); + + kani::assume(align.is_power_of_two()); + kani::assume(align < DstLayout::THEORETICAL_MAX_ALIGN); + + // For testing purposes, we most care about instantiations of + // `DstLayout` that can correspond to actual Rust types. We use + // `Layout` to verify that our `DstLayout` satisfies the validity + // conditions of Rust layouts. + kani::assume( + match size_info { + SizeInfo::Sized { _size } => Layout::from_size_align(_size, align.get()), + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) => { + // `SliceDst`` cannot encode an exact size, but we know + // it is at least `_offset` bytes. + Layout::from_size_align(_offset, align.get()) + } + } + .is_ok(), + ); + + Self { align: align, size_info: size_info } + } + } + + impl kani::Arbitrary for SizeInfo { + fn any() -> Self { + let is_sized: bool = kani::any(); + + match is_sized { + true => { + let size: usize = kani::any(); + + kani::assume(size <= isize::MAX as _); + + SizeInfo::Sized { _size: size } + } + false => SizeInfo::SliceDst(kani::any()), + } + } + } + + impl kani::Arbitrary for TrailingSliceLayout { + fn any() -> Self { + let elem_size: usize = kani::any(); + let offset: usize = kani::any(); + + kani::assume(elem_size < isize::MAX as _); + kani::assume(offset < isize::MAX as _); + + TrailingSliceLayout { _elem_size: elem_size, _offset: offset } + } + } + + #[kani::proof] + fn prove_dst_layout_extend() { + use crate::util::{core_layout::padding_needed_for, max, min}; + + let base: DstLayout = kani::any(); + let field: DstLayout = kani::any(); + let packed: Option<NonZeroUsize> = kani::any(); + + if let Some(max_align) = packed { + kani::assume(max_align.is_power_of_two()); + kani::assume(base.align <= max_align); + } + + // The base can only be extended if it's sized. + kani::assume(matches!(base.size_info, SizeInfo::Sized { .. })); + let base_size = if let SizeInfo::Sized { _size: size } = base.size_info { + size + } else { + unreachable!(); + }; + + // Under the above conditions, `DstLayout::extend` will not panic. + let composite = base.extend(field, packed); + + // The field's alignment is clamped by `max_align` (i.e., the + // `packed` attribute, if any) [1]. + // + // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: + // + // The alignments of each field, for the purpose of positioning + // fields, is the smaller of the specified alignment and the + // alignment of the field's type. + let field_align = min(field.align, packed.unwrap_or(DstLayout::THEORETICAL_MAX_ALIGN)); + + // The struct's alignment is the maximum of its previous alignment and + // `field_align`. + assert_eq!(composite.align, max(base.align, field_align)); + + // Compute the minimum amount of inter-field padding needed to + // satisfy the field's alignment, and offset of the trailing field. + // [1] + // + // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: + // + // Inter-field padding is guaranteed to be the minimum required in + // order to satisfy each field's (possibly altered) alignment. + let padding = padding_needed_for(base_size, field_align); + let offset = base_size + padding; + + // For testing purposes, we'll also construct `alloc::Layout` + // stand-ins for `DstLayout`, and show that `extend` behaves + // comparably on both types. + let base_analog = Layout::from_size_align(base_size, base.align.get()).unwrap(); + + match field.size_info { + SizeInfo::Sized { _size: field_size } => { + if let SizeInfo::Sized { _size: composite_size } = composite.size_info { + // If the trailing field is sized, the resulting layout + // will be sized. Its size will be the sum of the + // preceeding layout, the size of the new field, and the + // size of inter-field padding between the two. + assert_eq!(composite_size, offset + field_size); + + let field_analog = + Layout::from_size_align(field_size, field_align.get()).unwrap(); + + if let Ok((actual_composite, actual_offset)) = base_analog.extend(field_analog) + { + assert_eq!(actual_offset, offset); + assert_eq!(actual_composite.size(), composite_size); + assert_eq!(actual_composite.align(), composite.align.get()); + } else { + // An error here reflects that composite of `base` + // and `field` cannot correspond to a real Rust type + // fragment, because such a fragment would violate + // the basic invariants of a valid Rust layout. At + // the time of writing, `DstLayout` is a little more + // permissive than `Layout`, so we don't assert + // anything in this branch (e.g., unreachability). + } + } else { + panic!("The composite of two sized layouts must be sized.") + } + } + SizeInfo::SliceDst(TrailingSliceLayout { + _offset: field_offset, + _elem_size: field_elem_size, + }) => { + if let SizeInfo::SliceDst(TrailingSliceLayout { + _offset: composite_offset, + _elem_size: composite_elem_size, + }) = composite.size_info + { + // The offset of the trailing slice component is the sum + // of the offset of the trailing field and the trailing + // slice offset within that field. + assert_eq!(composite_offset, offset + field_offset); + // The elem size is unchanged. + assert_eq!(composite_elem_size, field_elem_size); + + let field_analog = + Layout::from_size_align(field_offset, field_align.get()).unwrap(); + + if let Ok((actual_composite, actual_offset)) = base_analog.extend(field_analog) + { + assert_eq!(actual_offset, offset); + assert_eq!(actual_composite.size(), composite_offset); + assert_eq!(actual_composite.align(), composite.align.get()); + } else { + // An error here reflects that composite of `base` + // and `field` cannot correspond to a real Rust type + // fragment, because such a fragment would violate + // the basic invariants of a valid Rust layout. At + // the time of writing, `DstLayout` is a little more + // permissive than `Layout`, so we don't assert + // anything in this branch (e.g., unreachability). + } + } else { + panic!("The extension of a layout with a DST must result in a DST.") + } + } + } + } - assert_impls!(ManuallyDrop<u8>: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(ManuallyDrop<[u8]>: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(ManuallyDrop<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); - assert_impls!(ManuallyDrop<[NotZerocopy]>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + #[kani::proof] + #[kani::should_panic] + fn prove_dst_layout_extend_dst_panics() { + let base: DstLayout = kani::any(); + let field: DstLayout = kani::any(); + let packed: Option<NonZeroUsize> = kani::any(); - assert_impls!(MaybeUninit<u8>: FromZeroes, FromBytes, Unaligned, !AsBytes); - assert_impls!(MaybeUninit<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + if let Some(max_align) = packed { + kani::assume(max_align.is_power_of_two()); + kani::assume(base.align <= max_align); + } - assert_impls!(Wrapping<u8>: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(Wrapping<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + kani::assume(matches!(base.size_info, SizeInfo::SliceDst(..))); - assert_impls!(Unalign<u8>: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(Unalign<NotZerocopy>: Unaligned, !FromZeroes, !FromBytes, !AsBytes); + let _ = base.extend(field, packed); + } - assert_impls!([u8]: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!([NotZerocopy]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); - assert_impls!([u8; 0]: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!([NotZerocopy; 0]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); - assert_impls!([u8; 1]: FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!([NotZerocopy; 1]: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + #[kani::proof] + fn prove_dst_layout_pad_to_align() { + use crate::util::core_layout::padding_needed_for; + + let layout: DstLayout = kani::any(); + + let padded: DstLayout = layout.pad_to_align(); + + // Calling `pad_to_align` does not alter the `DstLayout`'s alignment. + assert_eq!(padded.align, layout.align); + + if let SizeInfo::Sized { _size: unpadded_size } = layout.size_info { + if let SizeInfo::Sized { _size: padded_size } = padded.size_info { + // If the layout is sized, it will remain sized after padding is + // added. Its sum will be its unpadded size and the size of the + // trailing padding needed to satisfy its alignment + // requirements. + let padding = padding_needed_for(unpadded_size, layout.align); + assert_eq!(padded_size, unpadded_size + padding); + + // Prove that calling `DstLayout::pad_to_align` behaves + // identically to `Layout::pad_to_align`. + let layout_analog = + Layout::from_size_align(unpadded_size, layout.align.get()).unwrap(); + let padded_analog = layout_analog.pad_to_align(); + assert_eq!(padded_analog.align(), layout.align.get()); + assert_eq!(padded_analog.size(), padded_size); + } else { + panic!("The padding of a sized layout must result in a sized layout.") + } + } else { + // If the layout is a DST, padding cannot be statically added. + assert_eq!(padded.size_info, layout.size_info); + } } } diff --git a/src/macro_util.rs b/src/macro_util.rs new file mode 100644 index 0000000..24fec4f --- /dev/null +++ b/src/macro_util.rs @@ -0,0 +1,670 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +//! Utilities used by macros and by `zerocopy-derive`. +//! +//! These are defined here `zerocopy` rather than in code generated by macros or +//! by `zerocopy-derive` so that they can be compiled once rather than +//! recompiled for every invocation (e.g., if they were defined in generated +//! code, then deriving `AsBytes` and `FromBytes` on three different types would +//! result in the code in question being emitted and compiled six different +//! times). + +#![allow(missing_debug_implementations)] + +use core::{marker::PhantomData, mem::ManuallyDrop}; + +// TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this +// `cfg` when `size_of_val_raw` is stabilized. +#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +use core::ptr::{self, NonNull}; + +/// A compile-time check that should be one particular value. +pub trait ShouldBe<const VALUE: bool> {} + +/// A struct for checking whether `T` contains padding. +pub struct HasPadding<T: ?Sized, const VALUE: bool>(PhantomData<T>); + +impl<T: ?Sized, const VALUE: bool> ShouldBe<VALUE> for HasPadding<T, VALUE> {} + +/// A type whose size is equal to `align_of::<T>()`. +#[repr(C)] +pub struct AlignOf<T> { + // This field ensures that: + // - The size is always at least 1 (the minimum possible alignment). + // - If the alignment is greater than 1, Rust has to round up to the next + // multiple of it in order to make sure that `Align`'s size is a multiple + // of that alignment. Without this field, its size could be 0, which is a + // valid multiple of any alignment. + _u: u8, + _a: [T; 0], +} + +impl<T> AlignOf<T> { + #[inline(never)] // Make `missing_inline_in_public_items` happy. + pub fn into_t(self) -> T { + unreachable!() + } +} + +/// A type whose size is equal to `max(align_of::<T>(), align_of::<U>())`. +#[repr(C)] +pub union MaxAlignsOf<T, U> { + _t: ManuallyDrop<AlignOf<T>>, + _u: ManuallyDrop<AlignOf<U>>, +} + +impl<T, U> MaxAlignsOf<T, U> { + #[inline(never)] // Make `missing_inline_in_public_items` happy. + pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + unreachable!() + } +} + +const _64K: usize = 1 << 16; + +// TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this +// `cfg` when `size_of_val_raw` is stabilized. +#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[repr(C, align(65536))] +struct Aligned64kAllocation([u8; _64K]); + +/// A pointer to an aligned allocation of size 2^16. +/// +/// # Safety +/// +/// `ALIGNED_64K_ALLOCATION` is guaranteed to point to the entirety of an +/// allocation with size and alignment 2^16, and to have valid provenance. +// TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this +// `cfg` when `size_of_val_raw` is stabilized. +#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +pub const ALIGNED_64K_ALLOCATION: NonNull<[u8]> = { + const REF: &Aligned64kAllocation = &Aligned64kAllocation([0; _64K]); + let ptr: *const Aligned64kAllocation = REF; + let ptr: *const [u8] = ptr::slice_from_raw_parts(ptr.cast(), _64K); + // SAFETY: + // - `ptr` is derived from a Rust reference, which is guaranteed to be + // non-null. + // - `ptr` is derived from an `&Aligned64kAllocation`, which has size and + // alignment `_64K` as promised. Its length is initialized to `_64K`, + // which means that it refers to the entire allocation. + // - `ptr` is derived from a Rust reference, which is guaranteed to have + // valid provenance. + // + // TODO(#429): Once `NonNull::new_unchecked` docs document that it preserves + // provenance, cite those docs. + // TODO: Replace this `as` with `ptr.cast_mut()` once our MSRV >= 1.65 + #[allow(clippy::as_conversions)] + unsafe { + NonNull::new_unchecked(ptr as *mut _) + } +}; + +/// Computes the offset of the base of the field `$trailing_field_name` within +/// the type `$ty`. +/// +/// `trailing_field_offset!` produces code which is valid in a `const` context. +// TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this +// `cfg` when `size_of_val_raw` is stabilized. +#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! trailing_field_offset { + ($ty:ty, $trailing_field_name:tt) => {{ + let min_size = { + let zero_elems: *const [()] = + $crate::macro_util::core_reexport::ptr::slice_from_raw_parts( + $crate::macro_util::core_reexport::ptr::NonNull::<()>::dangling() + .as_ptr() + .cast_const(), + 0, + ); + // SAFETY: + // - If `$ty` is `Sized`, `size_of_val_raw` is always safe to call. + // - Otherwise: + // - If `$ty` is not a slice DST, this pointer conversion will + // fail due to "mismatched vtable kinds", and compilation will + // fail. + // - If `$ty` is a slice DST, the safety requirement is that "the + // length of the slice tail must be an initialized integer, and + // the size of the entire value (dynamic tail length + + // statically sized prefix) must fit in isize." The length is + // initialized to 0 above, and Rust guarantees that no type's + // minimum size may overflow `isize`. [1] + // + // [1] TODO(#429), + // TODO(https://github.com/rust-lang/unsafe-code-guidelines/issues/465#issuecomment-1782206516): + // Citation for this? + unsafe { + #[allow(clippy::as_conversions)] + $crate::macro_util::core_reexport::mem::size_of_val_raw(zero_elems as *const $ty) + } + }; + + assert!(min_size <= _64K); + + #[allow(clippy::as_conversions)] + let ptr = ALIGNED_64K_ALLOCATION.as_ptr() as *const $ty; + + // SAFETY: + // - Thanks to the preceding `assert!`, we know that the value with zero + // elements fits in `_64K` bytes, and thus in the allocation addressed + // by `ALIGNED_64K_ALLOCATION`. The offset of the trailing field is + // guaranteed to be no larger than this size, so this field projection + // is guaranteed to remain in-bounds of its allocation. + // - Because the minimum size is no larger than `_64K` bytes, and + // because an object's size must always be a multiple of its alignment + // [1], we know that `$ty`'s alignment is no larger than `_64K`. The + // allocation addressed by `ALIGNED_64K_ALLOCATION` is guaranteed to + // be aligned to `_64K`, so `ptr` is guaranteed to satisfy `$ty`'s + // alignment. + // + // Note that, as of [2], this requirement is technically unnecessary + // for Rust versions >= 1.75.0, but no harm in guaranteeing it anyway + // until we bump our MSRV. + // + // [1] Per https://doc.rust-lang.org/reference/type-layout.html: + // + // The size of a value is always a multiple of its alignment. + // + // [2] https://github.com/rust-lang/reference/pull/1387 + let field = unsafe { + $crate::macro_util::core_reexport::ptr::addr_of!((*ptr).$trailing_field_name) + }; + // SAFETY: + // - Both `ptr` and `field` are derived from the same allocated object. + // - By the preceding safety comment, `field` is in bounds of that + // allocated object. + // - The distance, in bytes, between `ptr` and `field` is required to be + // a multiple of the size of `u8`, which is trivially true because + // `u8`'s size is 1. + // - The distance, in bytes, cannot overflow `isize`. This is guaranteed + // because no allocated object can have a size larger than can fit in + // `isize`. [1] + // - The distance being in-bounds cannot rely on wrapping around the + // address space. This is guaranteed because the same is guaranteed of + // allocated objects. [1] + // + // [1] TODO(#429), TODO(https://github.com/rust-lang/rust/pull/116675): + // Once these are guaranteed in the Reference, cite it. + let offset = unsafe { field.cast::<u8>().offset_from(ptr.cast::<u8>()) }; + // Guaranteed not to be lossy: `field` comes after `ptr`, so the offset + // from `ptr` to `field` is guaranteed to be positive. + assert!(offset >= 0); + Some( + #[allow(clippy::as_conversions)] + { + offset as usize + }, + ) + }}; +} + +/// Computes alignment of `$ty: ?Sized`. +/// +/// `align_of!` produces code which is valid in a `const` context. +// TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this +// `cfg` when `size_of_val_raw` is stabilized. +#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! align_of { + ($ty:ty) => {{ + // SAFETY: `OffsetOfTrailingIsAlignment` is `repr(C)`, and its layout is + // guaranteed [1] to begin with the single-byte layout for `_byte`, + // followed by the padding needed to align `_trailing`, then the layout + // for `_trailing`, and finally any trailing padding bytes needed to + // correctly-align the entire struct. + // + // This macro computes the alignment of `$ty` by counting the number of + // bytes preceeding `_trailing`. For instance, if the alignment of `$ty` + // is `1`, then no padding is required align `_trailing` and it will be + // located immediately after `_byte` at offset 1. If the alignment of + // `$ty` is 2, then a single padding byte is required before + // `_trailing`, and `_trailing` will be located at offset 2. + + // This correspondence between offset and alignment holds for all valid + // Rust alignments, and we confirm this exhaustively (or, at least up to + // the maximum alignment supported by `trailing_field_offset!`) in + // `test_align_of_dst`. + // + // [1]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprc + + #[repr(C)] + struct OffsetOfTrailingIsAlignment { + _byte: u8, + _trailing: $ty, + } + + trailing_field_offset!(OffsetOfTrailingIsAlignment, _trailing) + }}; +} + +/// Does the struct type `$t` have padding? +/// +/// `$ts` is the list of the type of every field in `$t`. `$t` must be a +/// struct type, or else `struct_has_padding!`'s result may be meaningless. +/// +/// Note that `struct_has_padding!`'s results are independent of `repr` since +/// they only consider the size of the type and the sizes of the fields. +/// Whatever the repr, the size of the type already takes into account any +/// padding that the compiler has decided to add. Structs with well-defined +/// representations (such as `repr(C)`) can use this macro to check for padding. +/// Note that while this may yield some consistent value for some `repr(Rust)` +/// structs, it is not guaranteed across platforms or compilations. +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! struct_has_padding { + ($t:ty, $($ts:ty),*) => { + core::mem::size_of::<$t>() > 0 $(+ core::mem::size_of::<$ts>())* + }; +} + +/// Does the union type `$t` have padding? +/// +/// `$ts` is the list of the type of every field in `$t`. `$t` must be a +/// union type, or else `union_has_padding!`'s result may be meaningless. +/// +/// Note that `union_has_padding!`'s results are independent of `repr` since +/// they only consider the size of the type and the sizes of the fields. +/// Whatever the repr, the size of the type already takes into account any +/// padding that the compiler has decided to add. Unions with well-defined +/// representations (such as `repr(C)`) can use this macro to check for padding. +/// Note that while this may yield some consistent value for some `repr(Rust)` +/// unions, it is not guaranteed across platforms or compilations. +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! union_has_padding { + ($t:ty, $($ts:ty),*) => { + false $(|| core::mem::size_of::<$t>() != core::mem::size_of::<$ts>())* + }; +} + +/// Does `t` have alignment greater than or equal to `u`? If not, this macro +/// produces a compile error. It must be invoked in a dead codepath. This is +/// used in `transmute_ref!` and `transmute_mut!`. +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! assert_align_gt_eq { + ($t:ident, $u: ident) => {{ + // The comments here should be read in the context of this macro's + // invocations in `transmute_ref!` and `transmute_mut!`. + if false { + // The type wildcard in this bound is inferred to be `T` because + // `align_of.into_t()` is assigned to `t` (which has type `T`). + let align_of: $crate::macro_util::AlignOf<_> = unreachable!(); + $t = align_of.into_t(); + // `max_aligns` is inferred to have type `MaxAlignsOf<T, U>` because + // of the inferred types of `t` and `u`. + let mut max_aligns = $crate::macro_util::MaxAlignsOf::new($t, $u); + + // This transmute will only compile successfully if + // `align_of::<T>() == max(align_of::<T>(), align_of::<U>())` - in + // other words, if `align_of::<T>() >= align_of::<U>()`. + // + // SAFETY: This code is never run. + max_aligns = unsafe { $crate::macro_util::core_reexport::mem::transmute(align_of) }; + } else { + loop {} + } + }}; +} + +/// Do `t` and `u` have the same size? If not, this macro produces a compile +/// error. It must be invoked in a dead codepath. This is used in +/// `transmute_ref!` and `transmute_mut!`. +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! assert_size_eq { + ($t:ident, $u: ident) => {{ + // The comments here should be read in the context of this macro's + // invocations in `transmute_ref!` and `transmute_mut!`. + if false { + // SAFETY: This code is never run. + $u = unsafe { + // Clippy: It's okay to transmute a type to itself. + #[allow(clippy::useless_transmute)] + $crate::macro_util::core_reexport::mem::transmute($t) + }; + } else { + loop {} + } + }}; +} + +/// Transmutes a reference of one type to a reference of another type. +/// +/// # Safety +/// +/// The caller must guarantee that: +/// - `Src: AsBytes` +/// - `Dst: FromBytes` +/// - `size_of::<Src>() == size_of::<Dst>()` +/// - `align_of::<Src>() >= align_of::<Dst>()` +#[inline(always)] +pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + src: &'src Src, +) -> &'dst Dst { + let src: *const Src = src; + let dst = src.cast::<Dst>(); + // SAFETY: + // - We know that it is sound to view the target type of the input reference + // (`Src`) as the target type of the output reference (`Dst`) because the + // caller has guaranteed that `Src: AsBytes`, `Dst: FromBytes`, and + // `size_of::<Src>() == size_of::<Dst>()`. + // - We know that there are no `UnsafeCell`s, and thus we don't have to + // worry about `UnsafeCell` overlap, because `Src: AsBytes` and `Dst: + // FromBytes` both forbid `UnsafeCell`s. + // - The caller has guaranteed that alignment is not increased. + // - We know that the returned lifetime will not outlive the input lifetime + // thanks to the lifetime bounds on this function. + unsafe { &*dst } +} + +/// Transmutes a mutable reference of one type to a mutable reference of another +/// type. +/// +/// # Safety +/// +/// The caller must guarantee that: +/// - `Src: FromBytes + AsBytes` +/// - `Dst: FromBytes + AsBytes` +/// - `size_of::<Src>() == size_of::<Dst>()` +/// - `align_of::<Src>() >= align_of::<Dst>()` +#[inline(always)] +pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + src: &'src mut Src, +) -> &'dst mut Dst { + let src: *mut Src = src; + let dst = src.cast::<Dst>(); + // SAFETY: + // - We know that it is sound to view the target type of the input reference + // (`Src`) as the target type of the output reference (`Dst`) and + // vice-versa because the caller has guaranteed that `Src: FromBytes + + // AsBytes`, `Dst: FromBytes + AsBytes`, and `size_of::<Src>() == + // size_of::<Dst>()`. + // - We know that there are no `UnsafeCell`s, and thus we don't have to + // worry about `UnsafeCell` overlap, because `Src: FromBytes + AsBytes` + // and `Dst: FromBytes + AsBytes` forbid `UnsafeCell`s. + // - The caller has guaranteed that alignment is not increased. + // - We know that the returned lifetime will not outlive the input lifetime + // thanks to the lifetime bounds on this function. + unsafe { &mut *dst } +} + +// NOTE: We can't change this to a `pub use core as core_reexport` until [1] is +// fixed or we update to a semver-breaking version (as of this writing, 0.8.0) +// on the `main` branch. +// +// [1] https://github.com/obi1kenobi/cargo-semver-checks/issues/573 +pub mod core_reexport { + pub use core::*; + + pub mod mem { + pub use core::mem::*; + } +} + +#[cfg(test)] +mod tests { + use core::mem; + + use super::*; + use crate::util::testutil::*; + + #[test] + fn test_align_of() { + macro_rules! test { + ($ty:ty) => { + assert_eq!(mem::size_of::<AlignOf<$ty>>(), mem::align_of::<$ty>()); + }; + } + + test!(()); + test!(u8); + test!(AU64); + test!([AU64; 2]); + } + + #[test] + fn test_max_aligns_of() { + macro_rules! test { + ($t:ty, $u:ty) => { + assert_eq!( + mem::size_of::<MaxAlignsOf<$t, $u>>(), + core::cmp::max(mem::align_of::<$t>(), mem::align_of::<$u>()) + ); + }; + } + + test!(u8, u8); + test!(u8, AU64); + test!(AU64, u8); + } + + #[test] + fn test_typed_align_check() { + // Test that the type-based alignment check used in + // `assert_align_gt_eq!` behaves as expected. + + macro_rules! assert_t_align_gteq_u_align { + ($t:ty, $u:ty, $gteq:expr) => { + assert_eq!( + mem::size_of::<MaxAlignsOf<$t, $u>>() == mem::size_of::<AlignOf<$t>>(), + $gteq + ); + }; + } + + assert_t_align_gteq_u_align!(u8, u8, true); + assert_t_align_gteq_u_align!(AU64, AU64, true); + assert_t_align_gteq_u_align!(AU64, u8, true); + assert_t_align_gteq_u_align!(u8, AU64, false); + } + + // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove + // this `cfg` when `size_of_val_raw` is stabilized. + #[allow(clippy::decimal_literal_representation)] + #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[test] + fn test_trailing_field_offset() { + assert_eq!(mem::align_of::<Aligned64kAllocation>(), _64K); + + macro_rules! test { + (#[$cfg:meta] ($($ts:ty),* ; $trailing_field_ty:ty) => $expect:expr) => {{ + #[$cfg] + struct Test($($ts,)* $trailing_field_ty); + assert_eq!(test!(@offset $($ts),* ; $trailing_field_ty), $expect); + }}; + (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),* ; $trailing_field_ty:ty) => $expect:expr) => { + test!(#[$cfg] ($($ts),* ; $trailing_field_ty) => $expect); + test!($(#[$cfgs])* ($($ts),* ; $trailing_field_ty) => $expect); + }; + (@offset ; $_trailing:ty) => { trailing_field_offset!(Test, 0) }; + (@offset $_t:ty ; $_trailing:ty) => { trailing_field_offset!(Test, 1) }; + } + + test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; u8) => Some(0)); + test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; [u8]) => Some(0)); + test!(#[repr(C)] #[repr(packed)] (u8; u8) => Some(1)); + test!(#[repr(C)] (; AU64) => Some(0)); + test!(#[repr(C)] (; [AU64]) => Some(0)); + test!(#[repr(C)] (u8; AU64) => Some(8)); + test!(#[repr(C)] (u8; [AU64]) => Some(8)); + test!(#[repr(C)] (; Nested<u8, AU64>) => Some(0)); + test!(#[repr(C)] (; Nested<u8, [AU64]>) => Some(0)); + test!(#[repr(C)] (u8; Nested<u8, AU64>) => Some(8)); + test!(#[repr(C)] (u8; Nested<u8, [AU64]>) => Some(8)); + + // Test that `packed(N)` limits the offset of the trailing field. + test!(#[repr(C, packed( 1))] (u8; elain::Align< 2>) => Some( 1)); + test!(#[repr(C, packed( 2))] (u8; elain::Align< 4>) => Some( 2)); + test!(#[repr(C, packed( 4))] (u8; elain::Align< 8>) => Some( 4)); + test!(#[repr(C, packed( 8))] (u8; elain::Align< 16>) => Some( 8)); + test!(#[repr(C, packed( 16))] (u8; elain::Align< 32>) => Some( 16)); + test!(#[repr(C, packed( 32))] (u8; elain::Align< 64>) => Some( 32)); + test!(#[repr(C, packed( 64))] (u8; elain::Align< 128>) => Some( 64)); + test!(#[repr(C, packed( 128))] (u8; elain::Align< 256>) => Some( 128)); + test!(#[repr(C, packed( 256))] (u8; elain::Align< 512>) => Some( 256)); + test!(#[repr(C, packed( 512))] (u8; elain::Align< 1024>) => Some( 512)); + test!(#[repr(C, packed( 1024))] (u8; elain::Align< 2048>) => Some( 1024)); + test!(#[repr(C, packed( 2048))] (u8; elain::Align< 4096>) => Some( 2048)); + test!(#[repr(C, packed( 4096))] (u8; elain::Align< 8192>) => Some( 4096)); + test!(#[repr(C, packed( 8192))] (u8; elain::Align< 16384>) => Some( 8192)); + test!(#[repr(C, packed( 16384))] (u8; elain::Align< 32768>) => Some( 16384)); + test!(#[repr(C, packed( 32768))] (u8; elain::Align< 65536>) => Some( 32768)); + test!(#[repr(C, packed( 65536))] (u8; elain::Align< 131072>) => Some( 65536)); + /* Alignments above 65536 are not yet supported. + test!(#[repr(C, packed( 131072))] (u8; elain::Align< 262144>) => Some( 131072)); + test!(#[repr(C, packed( 262144))] (u8; elain::Align< 524288>) => Some( 262144)); + test!(#[repr(C, packed( 524288))] (u8; elain::Align< 1048576>) => Some( 524288)); + test!(#[repr(C, packed( 1048576))] (u8; elain::Align< 2097152>) => Some( 1048576)); + test!(#[repr(C, packed( 2097152))] (u8; elain::Align< 4194304>) => Some( 2097152)); + test!(#[repr(C, packed( 4194304))] (u8; elain::Align< 8388608>) => Some( 4194304)); + test!(#[repr(C, packed( 8388608))] (u8; elain::Align< 16777216>) => Some( 8388608)); + test!(#[repr(C, packed( 16777216))] (u8; elain::Align< 33554432>) => Some( 16777216)); + test!(#[repr(C, packed( 33554432))] (u8; elain::Align< 67108864>) => Some( 33554432)); + test!(#[repr(C, packed( 67108864))] (u8; elain::Align< 33554432>) => Some( 67108864)); + test!(#[repr(C, packed( 33554432))] (u8; elain::Align<134217728>) => Some( 33554432)); + test!(#[repr(C, packed(134217728))] (u8; elain::Align<268435456>) => Some(134217728)); + test!(#[repr(C, packed(268435456))] (u8; elain::Align<268435456>) => Some(268435456)); + */ + + // Test that `align(N)` does not limit the offset of the trailing field. + test!(#[repr(C, align( 1))] (u8; elain::Align< 2>) => Some( 2)); + test!(#[repr(C, align( 2))] (u8; elain::Align< 4>) => Some( 4)); + test!(#[repr(C, align( 4))] (u8; elain::Align< 8>) => Some( 8)); + test!(#[repr(C, align( 8))] (u8; elain::Align< 16>) => Some( 16)); + test!(#[repr(C, align( 16))] (u8; elain::Align< 32>) => Some( 32)); + test!(#[repr(C, align( 32))] (u8; elain::Align< 64>) => Some( 64)); + test!(#[repr(C, align( 64))] (u8; elain::Align< 128>) => Some( 128)); + test!(#[repr(C, align( 128))] (u8; elain::Align< 256>) => Some( 256)); + test!(#[repr(C, align( 256))] (u8; elain::Align< 512>) => Some( 512)); + test!(#[repr(C, align( 512))] (u8; elain::Align< 1024>) => Some( 1024)); + test!(#[repr(C, align( 1024))] (u8; elain::Align< 2048>) => Some( 2048)); + test!(#[repr(C, align( 2048))] (u8; elain::Align< 4096>) => Some( 4096)); + test!(#[repr(C, align( 4096))] (u8; elain::Align< 8192>) => Some( 8192)); + test!(#[repr(C, align( 8192))] (u8; elain::Align< 16384>) => Some( 16384)); + test!(#[repr(C, align( 16384))] (u8; elain::Align< 32768>) => Some( 32768)); + test!(#[repr(C, align( 32768))] (u8; elain::Align< 65536>) => Some( 65536)); + /* Alignments above 65536 are not yet supported. + test!(#[repr(C, align( 65536))] (u8; elain::Align< 131072>) => Some( 131072)); + test!(#[repr(C, align( 131072))] (u8; elain::Align< 262144>) => Some( 262144)); + test!(#[repr(C, align( 262144))] (u8; elain::Align< 524288>) => Some( 524288)); + test!(#[repr(C, align( 524288))] (u8; elain::Align< 1048576>) => Some( 1048576)); + test!(#[repr(C, align( 1048576))] (u8; elain::Align< 2097152>) => Some( 2097152)); + test!(#[repr(C, align( 2097152))] (u8; elain::Align< 4194304>) => Some( 4194304)); + test!(#[repr(C, align( 4194304))] (u8; elain::Align< 8388608>) => Some( 8388608)); + test!(#[repr(C, align( 8388608))] (u8; elain::Align< 16777216>) => Some( 16777216)); + test!(#[repr(C, align( 16777216))] (u8; elain::Align< 33554432>) => Some( 33554432)); + test!(#[repr(C, align( 33554432))] (u8; elain::Align< 67108864>) => Some( 67108864)); + test!(#[repr(C, align( 67108864))] (u8; elain::Align< 33554432>) => Some( 33554432)); + test!(#[repr(C, align( 33554432))] (u8; elain::Align<134217728>) => Some(134217728)); + test!(#[repr(C, align(134217728))] (u8; elain::Align<268435456>) => Some(268435456)); + */ + } + + // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove + // this `cfg` when `size_of_val_raw` is stabilized. + #[allow(clippy::decimal_literal_representation)] + #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[test] + fn test_align_of_dst() { + // Test that `align_of!` correctly computes the alignment of DSTs. + assert_eq!(align_of!([elain::Align<1>]), Some(1)); + assert_eq!(align_of!([elain::Align<2>]), Some(2)); + assert_eq!(align_of!([elain::Align<4>]), Some(4)); + assert_eq!(align_of!([elain::Align<8>]), Some(8)); + assert_eq!(align_of!([elain::Align<16>]), Some(16)); + assert_eq!(align_of!([elain::Align<32>]), Some(32)); + assert_eq!(align_of!([elain::Align<64>]), Some(64)); + assert_eq!(align_of!([elain::Align<128>]), Some(128)); + assert_eq!(align_of!([elain::Align<256>]), Some(256)); + assert_eq!(align_of!([elain::Align<512>]), Some(512)); + assert_eq!(align_of!([elain::Align<1024>]), Some(1024)); + assert_eq!(align_of!([elain::Align<2048>]), Some(2048)); + assert_eq!(align_of!([elain::Align<4096>]), Some(4096)); + assert_eq!(align_of!([elain::Align<8192>]), Some(8192)); + assert_eq!(align_of!([elain::Align<16384>]), Some(16384)); + assert_eq!(align_of!([elain::Align<32768>]), Some(32768)); + assert_eq!(align_of!([elain::Align<65536>]), Some(65536)); + /* Alignments above 65536 are not yet supported. + assert_eq!(align_of!([elain::Align<131072>]), Some(131072)); + assert_eq!(align_of!([elain::Align<262144>]), Some(262144)); + assert_eq!(align_of!([elain::Align<524288>]), Some(524288)); + assert_eq!(align_of!([elain::Align<1048576>]), Some(1048576)); + assert_eq!(align_of!([elain::Align<2097152>]), Some(2097152)); + assert_eq!(align_of!([elain::Align<4194304>]), Some(4194304)); + assert_eq!(align_of!([elain::Align<8388608>]), Some(8388608)); + assert_eq!(align_of!([elain::Align<16777216>]), Some(16777216)); + assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); + assert_eq!(align_of!([elain::Align<67108864>]), Some(67108864)); + assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); + assert_eq!(align_of!([elain::Align<134217728>]), Some(134217728)); + assert_eq!(align_of!([elain::Align<268435456>]), Some(268435456)); + */ + } + + #[test] + fn test_struct_has_padding() { + // Test that, for each provided repr, `struct_has_padding!` reports the + // expected value. + macro_rules! test { + (#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{ + #[$cfg] + struct Test($($ts),*); + assert_eq!(struct_has_padding!(Test, $($ts),*), $expect); + }}; + (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => { + test!(#[$cfg] ($($ts),*) => $expect); + test!($(#[$cfgs])* ($($ts),*) => $expect); + }; + } + + test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] () => false); + test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8) => false); + test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8, ()) => false); + test!(#[repr(C)] #[repr(packed)] (u8, u8) => false); + + test!(#[repr(C)] (u8, AU64) => true); + // Rust won't let you put `#[repr(packed)]` on a type which contains a + // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. + // It's not ideal, but it definitely has align > 1 on /some/ of our CI + // targets, and this isn't a particularly complex macro we're testing + // anyway. + test!(#[repr(packed)] (u8, u64) => false); + } + + #[test] + fn test_union_has_padding() { + // Test that, for each provided repr, `union_has_padding!` reports the + // expected value. + macro_rules! test { + (#[$cfg:meta] {$($fs:ident: $ts:ty),*} => $expect:expr) => {{ + #[$cfg] + #[allow(unused)] // fields are never read + union Test{ $($fs: $ts),* } + assert_eq!(union_has_padding!(Test, $($ts),*), $expect); + }}; + (#[$cfg:meta] $(#[$cfgs:meta])* {$($fs:ident: $ts:ty),*} => $expect:expr) => { + test!(#[$cfg] {$($fs: $ts),*} => $expect); + test!($(#[$cfgs])* {$($fs: $ts),*} => $expect); + }; + } + + test!(#[repr(C)] #[repr(packed)] {a: u8} => false); + test!(#[repr(C)] #[repr(packed)] {a: u8, b: u8} => false); + + // Rust won't let you put `#[repr(packed)]` on a type which contains a + // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. + // It's not ideal, but it definitely has align > 1 on /some/ of our CI + // targets, and this isn't a particularly complex macro we're testing + // anyway. + test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => true); + } +} diff --git a/src/macros.rs b/src/macros.rs index aebc8d6..2da78af 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,6 +1,10 @@ -// Copyright 2023 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. /// Documents multiple unsafe blocks with a single safety comment. /// @@ -22,20 +26,46 @@ /// The macro invocations are emitted, each decorated with the following /// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`. macro_rules! safety_comment { - (#[doc = r" SAFETY:"] $($(#[doc = $_doc:literal])* $macro:ident!$args:tt;)*) => { - #[allow(clippy::undocumented_unsafe_blocks)] - const _: () = { $($macro!$args;)* }; + (#[doc = r" SAFETY:"] $($(#[$attr:meta])* $macro:ident!$args:tt;)*) => { + #[allow(clippy::undocumented_unsafe_blocks, unused_attributes)] + const _: () = { $($(#[$attr])* $macro!$args;)* }; } } /// Unsafely implements trait(s) for a type. +/// +/// # Safety +/// +/// The trait impl must be sound. +/// +/// When implementing `TryFromBytes`: +/// - If no `is_bit_valid` impl is provided, then it must be valid for +/// `is_bit_valid` to unconditionally return `true`. In other words, it must +/// be the case that any initialized sequence of bytes constitutes a valid +/// instance of `$ty`. +/// - If an `is_bit_valid` impl is provided, then: +/// - Regardless of whether the provided closure takes a `Ptr<$repr>` or +/// `&$repr` argument, it must be the case that, given `t: *mut $ty` and +/// `let r = t as *mut $repr`, `r` refers to an object of equal or lesser +/// size than the object referred to by `t`. +/// - If the provided closure takes a `&$repr` argument, then given a `Ptr<'a, +/// $ty>` which satisfies the preconditions of +/// `TryFromBytes::<$ty>::is_bit_valid`, it must be guaranteed that the +/// memory referenced by that `Ptr` always contains a valid `$repr`. +/// - The alignment of `$repr` is less than or equal to the alignment of +/// `$ty`. +/// - The impl of `is_bit_valid` must only return `true` for its argument +/// `Ptr<$repr>` if the original `Ptr<$ty>` refers to a valid `$ty`. macro_rules! unsafe_impl { // Implement `$trait` for `$ty` with no bounds. - ($ty:ty: $trait:ty) => { - unsafe impl $trait for $ty { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} } + ($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + $(#[$attr])* + unsafe impl $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + } }; // Implement all `$traits` for `$ty` with no bounds. - ($ty:ty: $($traits:ty),*) => { + ($ty:ty: $($traits:ident),*) => { $( unsafe_impl!($ty: $traits); )* }; // This arm is identical to the following one, except it contains a @@ -64,40 +94,142 @@ macro_rules! unsafe_impl { // The following arm has the same behavior with the exception of the lack of // support for a leading `const` parameter. ( + $(#[$attr:meta])* const $constname:ident : $constty:ident $(,)? $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* - => $trait:ident for $ty:ty + => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)? ) => { unsafe_impl!( @inner + $(#[$attr])* @const $constname: $constty, $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)* - => $trait for $ty + => $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)? ); }; ( + $(#[$attr:meta])* $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* - => $trait:ident for $ty:ty + => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)? ) => { unsafe_impl!( @inner + $(#[$attr])* $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)* - => $trait for $ty + => $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)? ); }; ( @inner + $(#[$attr:meta])* $(@const $constname:ident : $constty:ident,)* $($tyvar:ident $(: $(? $optbound:ident +)* + $($bound:ident +)* )?,)* - => $trait:ident for $ty:ty + => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)? ) => { + $(#[$attr])* unsafe impl<$(const $constname: $constty,)* $($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: $(&$ref_repr)? $(Ptr<$ptr_repr>)?| $is_bit_valid)?); + } + }; + + (@method TryFromBytes ; |$candidate:ident: &$repr:ty| $is_bit_valid:expr) => { + #[inline] + unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool { + // SAFETY: + // - The argument to `cast_unsized` is `|p| p as *mut _` as required + // by that method's safety precondition. + // - The caller has promised that the cast results in an object of + // equal or lesser size. + // - The caller has promised that `$repr`'s alignment is less than + // or equal to `Self`'s alignment. + #[allow(clippy::as_conversions)] + let candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) }; + // SAFETY: + // - The caller has promised that the referenced memory region will + // contain a valid `$repr` for `'a`. + // - The memory may not be referenced by any mutable references. + // This is a precondition of `is_bit_valid`. + // - The memory may not be mutated even via `UnsafeCell`s. This is a + // precondition of `is_bit_valid`. + // - There must not exist any references to the same memory region + // which contain `UnsafeCell`s at byte ranges which are not + // identical to the byte ranges at which `T` contains + // `UnsafeCell`s. This is a precondition of `is_bit_valid`. + let $candidate: &$repr = unsafe { candidate.as_ref() }; + $is_bit_valid + } + }; + (@method TryFromBytes ; |$candidate:ident: Ptr<$repr:ty>| $is_bit_valid:expr) => { + #[inline] + unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool { + // SAFETY: + // - The argument to `cast_unsized` is `|p| p as *mut _` as required + // by that method's safety precondition. + // - The caller has promised that the cast results in an object of + // equal or lesser size. + // - The caller has promised that `$repr`'s alignment is less than + // or equal to `Self`'s alignment. + #[allow(clippy::as_conversions)] + let $candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) }; + $is_bit_valid + } + }; + (@method TryFromBytes) => { #[inline(always)] unsafe fn is_bit_valid(_: Ptr<'_, Self>) -> bool { true } }; + (@method $trait:ident) => { + #[allow(clippy::missing_inline_in_public_items)] + fn only_derive_is_allowed_to_implement_this_trait() {} + }; + (@method $trait:ident; |$_candidate:ident $(: &$_ref_repr:ty)? $(: NonNull<$_ptr_repr:ty>)?| $_is_bit_valid:expr) => { + compile_error!("Can't provide `is_bit_valid` impl for trait other than `TryFromBytes`"); + }; +} + +/// Implements a trait for a type, bounding on each memeber of the power set of +/// a set of type variables. This is useful for implementing traits for tuples +/// or `fn` types. +/// +/// The last argument is the name of a macro which will be called in every +/// `impl` block, and is expected to expand to the name of the type for which to +/// implement the trait. +/// +/// For example, the invocation: +/// ```ignore +/// unsafe_impl_for_power_set!(A, B => Foo for type!(...)) +/// ``` +/// ...expands to: +/// ```ignore +/// unsafe impl Foo for type!() { ... } +/// unsafe impl<B> Foo for type!(B) { ... } +/// unsafe impl<A, B> Foo for type!(A, B) { ... } +/// ``` +macro_rules! unsafe_impl_for_power_set { + ($first:ident $(, $rest:ident)* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => { + unsafe_impl_for_power_set!($($rest),* $(-> $ret)? => $trait for $macro!(...)); + unsafe_impl_for_power_set!(@impl $first $(, $rest)* $(-> $ret)? => $trait for $macro!(...)); + }; + ($(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => { + unsafe_impl_for_power_set!(@impl $(-> $ret)? => $trait for $macro!(...)); + }; + (@impl $($vars:ident),* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => { + unsafe impl<$($vars,)* $($ret)?> $trait for $macro!($($vars),* $(-> $ret)?) { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} } }; } +/// Expands to an `Option<extern "C" fn>` type with the given argument types and +/// return type. Designed for use with `unsafe_impl_for_power_set`. +macro_rules! opt_extern_c_fn { + ($($args:ident),* -> $ret:ident) => { Option<extern "C" fn($($args),*) -> $ret> }; +} + +/// Expands to a `Option<fn>` type with the given argument types and return +/// type. Designed for use with `unsafe_impl_for_power_set`. +macro_rules! opt_fn { + ($($args:ident),* -> $ret:ident) => { Option<fn($($args),*) -> $ret> }; +} + /// Implements trait(s) for a type or verifies the given implementation by /// referencing an existing (derived) implementation. /// @@ -204,11 +336,26 @@ macro_rules! impl_known_layout { }; ($($ty:ty),*) => { $(impl_known_layout!(@inner , => $ty);)* }; (@inner $(const $constvar:ident : $constty:ty)? , $($tyvar:ident $(: ?$optbound:ident)?)? => $ty:ty) => { - impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> sealed::KnownLayoutSealed for $ty {} - // SAFETY: Delegates safety to `DstLayout::for_type`. - unsafe impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> KnownLayout for $ty { - const LAYOUT: DstLayout = DstLayout::for_type::<$ty>(); - } + const _: () = { + use core::ptr::NonNull; + + // SAFETY: Delegates safety to `DstLayout::for_type`. + unsafe impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> KnownLayout for $ty { + #[allow(clippy::missing_inline_in_public_items)] + fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized {} + + const LAYOUT: DstLayout = DstLayout::for_type::<$ty>(); + + // SAFETY: `.cast` preserves address and provenance. + // + // TODO(#429): Add documentation to `.cast` that promises that + // it preserves provenance. + #[inline(always)] + fn raw_from_ptr_len(bytes: NonNull<u8>, _elems: usize) -> NonNull<Self> { + bytes.cast::<Self>() + } + } + }; }; } @@ -225,10 +372,30 @@ macro_rules! impl_known_layout { /// and this operation must preserve referent size (ie, `size_of_val_raw`). macro_rules! unsafe_impl_known_layout { ($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($repr:ty)] $ty:ty) => { - impl<$($tyvar: ?Sized + KnownLayout)?> sealed::KnownLayoutSealed for $ty {} - unsafe impl<$($tyvar: ?Sized + KnownLayout)?> KnownLayout for $ty { - const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT; - } + const _: () = { + use core::ptr::NonNull; + + unsafe impl<$($tyvar: ?Sized + KnownLayout)?> KnownLayout for $ty { + #[allow(clippy::missing_inline_in_public_items)] + fn only_derive_is_allowed_to_implement_this_trait() {} + + const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT; + + // SAFETY: All operations preserve address and provenance. + // Caller has promised that the `as` cast preserves size. + // + // TODO(#429): Add documentation to `NonNull::new_unchecked` + // that it preserves provenance. + #[inline(always)] + #[allow(unused_qualifications)] // for `core::ptr::NonNull` + fn raw_from_ptr_len(bytes: NonNull<u8>, elems: usize) -> NonNull<Self> { + #[allow(clippy::as_conversions)] + let ptr = <$repr>::raw_from_ptr_len(bytes, elems).as_ptr() as *mut Self; + // SAFETY: `ptr` was converted from `bytes`, which is non-null. + unsafe { NonNull::new_unchecked(ptr) } + } + } + }; }; } diff --git a/src/third_party/rust/LICENSE-APACHE b/src/third_party/rust/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/src/third_party/rust/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/src/third_party/rust/LICENSE-MIT b/src/third_party/rust/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/src/third_party/rust/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/third_party/rust/README.fuchsia b/src/third_party/rust/README.fuchsia new file mode 100644 index 0000000..e0a23dd --- /dev/null +++ b/src/third_party/rust/README.fuchsia @@ -0,0 +1,7 @@ +Name: rust +License File: LICENSE-APACHE +License File: LICENSE-MIT +Description: + +See https://github.com/google/zerocopy/pull/492 for an explanation of why this +file exists. diff --git a/src/third_party/rust/layout.rs b/src/third_party/rust/layout.rs new file mode 100644 index 0000000..19ef7c6 --- /dev/null +++ b/src/third_party/rust/layout.rs @@ -0,0 +1,45 @@ +use core::num::NonZeroUsize; + +/// Returns the amount of padding we must insert after `len` bytes to ensure +/// that the following address will satisfy `align` (measured in bytes). +/// +/// e.g., if `len` is 9, then `padding_needed_for(len, 4)` returns 3, because +/// that is the minimum number of bytes of padding required to get a 4-aligned +/// address (assuming that the corresponding memory block starts at a 4-aligned +/// address). +/// +/// The return value of this function has no meaning if `align` is not a +/// power-of-two. +/// +/// # Panics +/// +/// May panic if `align` is not a power of two. +// +// TODO(#419): Replace `len` with a witness type for region size. +#[allow(unused)] +#[inline(always)] +pub(crate) const fn padding_needed_for(len: usize, align: NonZeroUsize) -> usize { + // Rounded up value is: + // len_rounded_up = (len + align - 1) & !(align - 1); + // and then we return the padding difference: `len_rounded_up - len`. + // + // We use modular arithmetic throughout: + // + // 1. align is guaranteed to be > 0, so align - 1 is always + // valid. + // + // 2. `len + align - 1` can overflow by at most `align - 1`, + // so the &-mask with `!(align - 1)` will ensure that in the + // case of overflow, `len_rounded_up` will itself be 0. + // Thus the returned padding, when added to `len`, yields 0, + // which trivially satisfies the alignment `align`. + // + // (Of course, attempts to allocate blocks of memory whose + // size and padding overflow in the above manner should cause + // the allocator to yield an error anyway.) + + let align = align.get(); + debug_assert!(align.is_power_of_two()); + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + len_rounded_up.wrapping_sub(len) +} diff --git a/src/util.rs b/src/util.rs index ed810dc..b35cc07 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,8 +1,552 @@ -// Copyright 2023 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. -use core::mem; +#[path = "third_party/rust/layout.rs"] +pub(crate) mod core_layout; + +use core::{mem, num::NonZeroUsize}; + +pub(crate) mod ptr { + use core::{ + fmt::{Debug, Formatter}, + marker::PhantomData, + ptr::NonNull, + }; + + use crate::{util::AsAddress, KnownLayout, _CastType}; + + /// A raw pointer with more restrictions. + /// + /// `Ptr<T>` is similar to `NonNull<T>`, but it is more restrictive in the + /// following ways: + /// - It must derive from a valid allocation + /// - It must reference a byte range which is contained inside the + /// allocation from which it derives + /// - As a consequence, the byte range it references must have a size + /// which does not overflow `isize` + /// - It must satisfy `T`'s alignment requirement + /// + /// Thanks to these restrictions, it is easier to prove the soundness of + /// some operations using `Ptr`s. + /// + /// `Ptr<'a, T>` is [covariant] in `'a` and `T`. + /// + /// [covariant]: https://doc.rust-lang.org/reference/subtyping.html + pub struct Ptr<'a, T: 'a + ?Sized> { + // INVARIANTS: + // 1. `ptr` is derived from some valid Rust allocation, `A` + // 2. `ptr` has the same provenance as `A` + // 3. `ptr` addresses a byte range which is entirely contained in `A` + // 4. `ptr` addresses a byte range whose length fits in an `isize` + // 5. `ptr` addresses a byte range which does not wrap around the address + // space + // 6. `ptr` is validly-aligned for `T` + // 7. `A` is guaranteed to live for at least `'a` + // 8. `T: 'a` + ptr: NonNull<T>, + _lifetime: PhantomData<&'a ()>, + } + + impl<'a, T: ?Sized> Copy for Ptr<'a, T> {} + impl<'a, T: ?Sized> Clone for Ptr<'a, T> { + #[inline] + fn clone(&self) -> Self { + *self + } + } + + impl<'a, T: ?Sized> Ptr<'a, T> { + /// Returns a shared reference to the value. + /// + /// # Safety + /// + /// For the duration of `'a`: + /// - The referenced memory must contain a validly-initialized `T` for + /// the duration of `'a`. + /// - The referenced memory must not also be referenced by any mutable + /// references. + /// - The referenced memory must not be mutated, even via an + /// [`UnsafeCell`]. + /// - There must not exist any references to the same memory region + /// which contain `UnsafeCell`s at byte ranges which are not identical + /// to the byte ranges at which `T` contains `UnsafeCell`s. + /// + /// [`UnsafeCell`]: core::cell::UnsafeCell + // TODO(#429): The safety requirements are likely overly-restrictive. + // Notably, mutation via `UnsafeCell`s is probably fine. Once the rules + // are more clearly defined, we should relax the safety requirements. + // For an example of why this is subtle, see: + // https://github.com/rust-lang/unsafe-code-guidelines/issues/463#issuecomment-1736771593 + #[allow(unused)] + pub(crate) unsafe fn as_ref(&self) -> &'a T { + // SAFETY: + // - By invariant, `self.ptr` is properly-aligned for `T`. + // - By invariant, `self.ptr` is "dereferenceable" in that it points + // to a single allocation. + // - By invariant, the allocation is live for `'a`. + // - The caller promises that no mutable references exist to this + // region during `'a`. + // - The caller promises that `UnsafeCell`s match exactly. + // - The caller promises that no mutation will happen during `'a`, + // even via `UnsafeCell`s. + // - The caller promises that the memory region contains a + // validly-intialized `T`. + unsafe { self.ptr.as_ref() } + } + + /// Casts to a different (unsized) target type. + /// + /// # Safety + /// + /// The caller promises that + /// - `cast(p)` is implemented exactly as follows: `|p: *mut T| p as + /// *mut U`. + /// - The size of the object referenced by the resulting pointer is less + /// than or equal to the size of the object referenced by `self`. + /// - The alignment of `U` is less than or equal to the alignment of + /// `T`. + pub(crate) unsafe fn cast_unsized<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>( + self, + cast: F, + ) -> Ptr<'a, U> { + let ptr = cast(self.ptr.as_ptr()); + // SAFETY: Caller promises that `cast` is just an `as` cast. We call + // `cast` on `self.ptr.as_ptr()`, which is non-null by construction. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + // SAFETY: + // - By invariant, `self.ptr` is derived from some valid Rust + // allocation, and since `ptr` is just `self.ptr as *mut U`, so is + // `ptr`. + // - By invariant, `self.ptr` has the same provenance as `A`, and so + // the same is true of `ptr`. + // - By invariant, `self.ptr` addresses a byte range which is + // entirely contained in `A`, and so the same is true of `ptr`. + // - By invariant, `self.ptr` addresses a byte range whose length + // fits in an `isize`, and so the same is true of `ptr`. + // - By invariant, `self.ptr` addresses a byte range which does not + // wrap around the address space, and so the same is true of + // `ptr`. + // - By invariant, `self.ptr` is validly-aligned for `T`. Since + // `ptr` has the same address, and since the caller promises that + // the alignment of `U` is less than or equal to the alignment of + // `T`, `ptr` is validly-aligned for `U`. + // - By invariant, `A` is guaranteed to live for at least `'a`. + // - `U: 'a` + Ptr { ptr, _lifetime: PhantomData } + } + } + + impl<'a> Ptr<'a, [u8]> { + /// Attempts to cast `self` to a `U` using the given cast type. + /// + /// Returns `None` if the resulting `U` would be invalidly-aligned or if + /// no `U` can fit in `self`. On success, returns a pointer to the + /// largest-possible `U` which fits in `self`. + /// + /// # Safety + /// + /// The caller may assume that this implementation is correct, and may + /// rely on that assumption for the soundness of their code. In + /// particular, the caller may assume that, if `try_cast_into` returns + /// `Some((ptr, split_at))`, then: + /// - If this is a prefix cast, `ptr` refers to the byte range `[0, + /// split_at)` in `self`. + /// - If this is a suffix cast, `ptr` refers to the byte range + /// `[split_at, self.len())` in `self`. + /// + /// # Panics + /// + /// Panics if `U` is a DST whose trailing slice element is zero-sized. + pub(crate) fn try_cast_into<U: 'a + ?Sized + KnownLayout>( + &self, + cast_type: _CastType, + ) -> Option<(Ptr<'a, U>, usize)> { + // PANICS: By invariant, the byte range addressed by `self.ptr` does + // not wrap around the address space. This implies that the sum of + // the address (represented as a `usize`) and length do not overflow + // `usize`, as required by `validate_cast_and_convert_metadata`. + // Thus, this call to `validate_cast_and_convert_metadata` won't + // panic. + let (elems, split_at) = U::LAYOUT.validate_cast_and_convert_metadata( + AsAddress::addr(self.ptr.as_ptr()), + self.len(), + cast_type, + )?; + let offset = match cast_type { + _CastType::_Prefix => 0, + _CastType::_Suffix => split_at, + }; + + let ptr = self.ptr.cast::<u8>().as_ptr(); + // SAFETY: `offset` is either `0` or `split_at`. + // `validate_cast_and_convert_metadata` promises that `split_at` is + // in the range `[0, self.len()]`. Thus, in both cases, `offset` is + // in `[0, self.len()]`. Thus: + // - The resulting pointer is in or one byte past the end of the + // same byte range as `self.ptr`. Since, by invariant, `self.ptr` + // addresses a byte range entirely contained within a single + // allocation, the pointer resulting from this operation is within + // or one byte past the end of that same allocation. + // - By invariant, `self.len() <= isize::MAX`. Since `offset <= + // self.len()`, `offset <= isize::MAX`. + // - By invariant, `self.ptr` addresses a byte range which does not + // wrap around the address space. This means that the base pointer + // plus the `self.len()` does not overflow `usize`. Since `offset + // <= self.len()`, this addition does not overflow `usize`. + let base = unsafe { ptr.add(offset) }; + // SAFETY: Since `add` is not allowed to wrap around, the preceding line + // produces a pointer whose address is greater than or equal to that of + // `ptr`. Since `ptr` is a `NonNull`, `base` is also non-null. + let base = unsafe { NonNull::new_unchecked(base) }; + let ptr = U::raw_from_ptr_len(base, elems); + // SAFETY: + // - By invariant, `self.ptr` is derived from some valid Rust + // allocation, `A`, and has the same provenance as `A`. All + // operations performed on `self.ptr` and values derived from it + // in this method preserve provenance, so: + // - `ptr` is derived from a valid Rust allocation, `A`. + // - `ptr` has the same provenance as `A`. + // - `validate_cast_and_convert_metadata` promises that the object + // described by `elems` and `split_at` lives at a byte range which + // is a subset of the input byte range. Thus: + // - Since, by invariant, `self.ptr` addresses a byte range + // entirely contained in `A`, so does `ptr`. + // - Since, by invariant, `self.ptr` addresses a range whose + // length is not longer than `isize::MAX` bytes, so does `ptr`. + // - Since, by invariant, `self.ptr` addresses a range which does + // not wrap around the address space, so does `ptr`. + // - `validate_cast_and_convert_metadata` promises that the object + // described by `split_at` is validly-aligned for `U`. + // - By invariant on `self`, `A` is guaranteed to live for at least + // `'a`. + // - `U: 'a` by trait bound. + Some((Ptr { ptr, _lifetime: PhantomData }, split_at)) + } + + /// Attempts to cast `self` into a `U`, failing if all of the bytes of + /// `self` cannot be treated as a `U`. + /// + /// In particular, this method fails if `self` is not validly-aligned + /// for `U` or if `self`'s size is not a valid size for `U`. + /// + /// # Safety + /// + /// On success, the caller may assume that the returned pointer + /// references the same byte range as `self`. + #[allow(unused)] + #[inline(always)] + pub(crate) fn try_cast_into_no_leftover<U: 'a + ?Sized + KnownLayout>( + &self, + ) -> Option<Ptr<'a, U>> { + // TODO(#67): Remove this allow. See NonNulSlicelExt for more + // details. + #[allow(unstable_name_collisions)] + match self.try_cast_into(_CastType::_Prefix) { + Some((slf, split_at)) if split_at == self.len() => Some(slf), + Some(_) | None => None, + } + } + } + + impl<'a, T> Ptr<'a, [T]> { + /// The number of slice elements referenced by `self`. + /// + /// # Safety + /// + /// Unsafe code my rely on `len` satisfying the above contract. + fn len(&self) -> usize { + #[allow(clippy::as_conversions)] + let slc = self.ptr.as_ptr() as *const [()]; + // SAFETY: + // - `()` has alignment 1, so `slc` is trivially aligned. + // - `slc` was derived from a non-null pointer. + // - The size is 0 regardless of the length, so it is sound to + // materialize a reference regardless of location. + // - By invariant, `self.ptr` has valid provenance. + let slc = unsafe { &*slc }; + // This is correct because the preceding `as` cast preserves the + // number of slice elements. Per + // https://doc.rust-lang.org/nightly/reference/expressions/operator-expr.html#slice-dst-pointer-to-pointer-cast: + // + // For slice types like `[T]` and `[U]`, the raw pointer types + // `*const [T]`, `*mut [T]`, `*const [U]`, and `*mut [U]` encode + // the number of elements in this slice. Casts between these raw + // pointer types preserve the number of elements. Note that, as a + // consequence, such casts do *not* necessarily preserve the size + // of the pointer's referent (e.g., casting `*const [u16]` to + // `*const [u8]` will result in a raw pointer which refers to an + // object of half the size of the original). The same holds for + // `str` and any compound type whose unsized tail is a slice type, + // such as struct `Foo(i32, [u8])` or `(u64, Foo)`. + // + // TODO(#429), + // TODO(https://github.com/rust-lang/reference/pull/1417): Once this + // text is available on the Stable docs, cite those instead of the + // Nightly docs. + slc.len() + } + + pub(crate) fn iter(&self) -> impl Iterator<Item = Ptr<'a, T>> { + // TODO(#429): Once `NonNull::cast` documents that it preserves + // provenance, cite those docs. + let base = self.ptr.cast::<T>().as_ptr(); + (0..self.len()).map(move |i| { + // TODO(https://github.com/rust-lang/rust/issues/74265): Use + // `NonNull::get_unchecked_mut`. + + // SAFETY: If the following conditions are not satisfied + // `pointer::cast` may induce Undefined Behavior [1]: + // > 1. Both the starting and resulting pointer must be either + // > in bounds or one byte past the end of the same allocated + // > object. + // > 2. The computed offset, in bytes, cannot overflow an + // > `isize`. + // > 3. The offset being in bounds cannot rely on “wrapping + // > around” the address space. That is, the + // > infinite-precision sum must fit in a `usize`. + // + // [1] https://doc.rust-lang.org/std/primitive.pointer.html#method.add + // + // We satisfy all three of these conditions here: + // 1. `base` (by invariant on `self`) points to an allocated + // object. By contract, `self.len()` accurately reflects the + // number of elements in the slice. `i` is in bounds of + // `c.len()` by construction, and so the result of this + // addition cannot overflow past the end of the allocation + // referred to by `c`. + // 2. By invariant on `Ptr`, `self` addresses a byte range whose + // length fits in an `isize`. Since `elem` is contained in + // `self`, the computed offset of `elem` must fit within + // `isize.` + // 3. By invariant on `Ptr`, `self` addresses a byte range which + // does not wrap around the address space. Since `elem` is + // contained in `self`, the computed offset of `elem` must + // wrap around the address space. + // + // TODO(#429): Once `pointer::add` documents that it preserves + // provenance, cite those docs. + let elem = unsafe { base.add(i) }; + + // SAFETY: + // - `elem` must not be null. `base` is constructed from a + // `NonNull` pointer, and the addition that produces `elem` + // must not overflow or wrap around, so `elem >= base > 0`. + // + // TODO(#429): Once `NonNull::new_unchecked` documents that it + // preserves provenance, cite those docs. + let elem = unsafe { NonNull::new_unchecked(elem) }; + + // SAFETY: The safety invariants of `Ptr` (see definition) are + // satisfied: + // 1. `elem` is derived from a valid Rust allocation, because + // `self` is derived from a valid Rust allocation, by + // invariant on `Ptr` + // 2. `elem` has the same provenance as `self`, because it + // derived from `self` using a series of + // provenance-preserving operations + // 3. `elem` is entirely contained in the allocation of `self` + // (see above) + // 4. `elem` addresses a byte range whose length fits in an + // `isize` (see above) + // 5. `elem` addresses a byte range which does not wrap around + // the address space (see above) + // 6. `elem` is validly-aligned for `T`. `self`, which + // represents a `[T]` is validly aligned for `T`, and `elem` + // is an element within that `[T]` + // 7. The allocation of `elem` is guaranteed to live for at + // least `'a`, because `elem` is entirely contained in + // `self`, which lives for at least `'a` by invariant on + // `Ptr`. + // 8. `T: 'a`, because `elem` is an element within `[T]`, and + // `[T]: 'a` by invariant on `Ptr` + Ptr { ptr: elem, _lifetime: PhantomData } + }) + } + } + + impl<'a, T: 'a + ?Sized> From<&'a T> for Ptr<'a, T> { + #[inline(always)] + fn from(t: &'a T) -> Ptr<'a, T> { + // SAFETY: `t` points to a valid Rust allocation, `A`, by + // construction. Thus: + // - `ptr` is derived from `A` + // - Since we use `NonNull::from`, which preserves provenance, `ptr` + // has the same provenance as `A` + // - Since `NonNull::from` creates a pointer which addresses the + // same bytes as `t`, `ptr` addresses a byte range entirely + // contained in (in this case, identical to) `A` + // - Since `t: &T`, it addresses no more than `isize::MAX` bytes [1] + // - Since `t: &T`, it addresses a byte range which does not wrap + // around the address space [2] + // - Since it is constructed from a valid `&T`, `ptr` is + // validly-aligned for `T` + // - Since `t: &'a T`, the allocation `A` is guaranteed to live for + // at least `'a` + // - `T: 'a` by trait bound + // + // TODO(#429), + // TODO(https://github.com/rust-lang/rust/issues/116181): Once it's + // documented, reference the guarantee that `NonNull::from` + // preserves provenance. + // + // TODO(#429), + // TODO(https://github.com/rust-lang/unsafe-code-guidelines/issues/465): + // - [1] Where does the reference document that allocations fit in + // `isize`? + // - [2] Where does the reference document that allocations don't + // wrap around the address space? + Ptr { ptr: NonNull::from(t), _lifetime: PhantomData } + } + } + + impl<'a, T: 'a + ?Sized> Debug for Ptr<'a, T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + self.ptr.fmt(f) + } + } + + #[cfg(test)] + mod tests { + use core::mem::{self, MaybeUninit}; + + use super::*; + use crate::{util::testutil::AU64, FromBytes}; + + #[test] + fn test_ptrtry_cast_into_soundness() { + // This test is designed so that if `Ptr::try_cast_into_xxx` are + // buggy, it will manifest as unsoundness that Miri can detect. + + // - If `size_of::<T>() == 0`, `N == 4` + // - Else, `N == 4 * size_of::<T>()` + fn test<const N: usize, T: ?Sized + KnownLayout + FromBytes>() { + let mut bytes = [MaybeUninit::<u8>::uninit(); N]; + let initialized = [MaybeUninit::new(0u8); N]; + for start in 0..=bytes.len() { + for end in start..=bytes.len() { + // Set all bytes to uninitialized other than those in + // the range we're going to pass to `try_cast_from`. + // This allows Miri to detect out-of-bounds reads + // because they read uninitialized memory. Without this, + // some out-of-bounds reads would still be in-bounds of + // `bytes`, and so might spuriously be accepted. + bytes = [MaybeUninit::<u8>::uninit(); N]; + let bytes = &mut bytes[start..end]; + // Initialize only the byte range we're going to pass to + // `try_cast_from`. + bytes.copy_from_slice(&initialized[start..end]); + + let bytes = { + let bytes: *const [MaybeUninit<u8>] = bytes; + #[allow(clippy::as_conversions)] + let bytes = bytes as *const [u8]; + // SAFETY: We just initialized these bytes to valid + // `u8`s. + unsafe { &*bytes } + }; + + /// # Safety + /// + /// - `slf` must reference a byte range which is + /// entirely initialized. + /// - `slf` must reference a byte range which is only + /// referenced by shared references which do not + /// contain `UnsafeCell`s during its lifetime. + unsafe fn validate_and_get_len<T: ?Sized + KnownLayout + FromBytes>( + slf: Ptr<'_, T>, + ) -> usize { + // SAFETY: + // - Since all bytes in `slf` are initialized and + // `T: FromBytes`, `slf` contains a valid `T`. + // - The caller promises that the referenced memory + // is not also referenced by any mutable + // references. + // - The caller promises that the referenced memory + // is not also referenced as a type which contains + // `UnsafeCell`s. + let t = unsafe { slf.as_ref() }; + + let bytes = { + let len = mem::size_of_val(t); + let t: *const T = t; + // SAFETY: + // - We know `t`'s bytes are all initialized + // because we just read it from `slf`, which + // points to an initialized range of bytes. If + // there's a bug and this doesn't hold, then + // that's exactly what we're hoping Miri will + // catch! + // - Since `T: FromBytes`, `T` doesn't contain + // any `UnsafeCell`s, so it's okay for `t: T` + // and a `&[u8]` to the same memory to be + // alive concurrently. + unsafe { core::slice::from_raw_parts(t.cast::<u8>(), len) } + }; + + // This assertion ensures that `t`'s bytes are read + // and compared to another value, which in turn + // ensures that Miri gets a chance to notice if any + // of `t`'s bytes are uninitialized, which they + // shouldn't be (see the comment above). + assert_eq!(bytes, vec![0u8; bytes.len()]); + + mem::size_of_val(t) + } + + for cast_type in [_CastType::_Prefix, _CastType::_Suffix] { + if let Some((slf, split_at)) = + Ptr::from(bytes).try_cast_into::<T>(cast_type) + { + // SAFETY: All bytes in `bytes` have been + // initialized. + let len = unsafe { validate_and_get_len(slf) }; + match cast_type { + _CastType::_Prefix => assert_eq!(split_at, len), + _CastType::_Suffix => assert_eq!(split_at, bytes.len() - len), + } + } + } + + if let Some(slf) = Ptr::from(bytes).try_cast_into_no_leftover::<T>() { + // SAFETY: All bytes in `bytes` have been + // initialized. + let len = unsafe { validate_and_get_len(slf) }; + assert_eq!(len, bytes.len()); + } + } + } + } + + macro_rules! test { + ($($ty:ty),*) => { + $({ + const S: usize = core::mem::size_of::<$ty>(); + const N: usize = if S == 0 { 4 } else { S * 4 }; + test::<N, $ty>(); + // We don't support casting into DSTs whose trailing slice + // element is a ZST. + if S > 0 { + test::<N, [$ty]>(); + } + // TODO: Test with a slice DST once we have any that + // implement `KnownLayout + FromBytes`. + })* + }; + } + + test!(()); + test!(u8, u16, u32, u64, u128, usize, AU64); + test!(i8, i16, i32, i64, i128, isize); + test!(f32, f64); + } + } +} pub(crate) trait AsAddress { fn addr(self) -> usize; @@ -27,10 +571,11 @@ impl<'a, T: ?Sized> AsAddress for &'a mut T { impl<T: ?Sized> AsAddress for *const T { #[inline(always)] fn addr(self) -> usize { - // TODO(https://github.com/rust-lang/rust/issues/95228): Use `.addr()` - // instead of `as usize` once it's stable, and get rid of this `allow`. - // Currently, `as usize` is the only way to accomplish this. + // TODO(#181), TODO(https://github.com/rust-lang/rust/issues/95228): Use + // `.addr()` instead of `as usize` once it's stable, and get rid of this + // `allow`. Currently, `as usize` is the only way to accomplish this. #[allow(clippy::as_conversions)] + #[cfg_attr(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, allow(lossy_provenance_casts))] return self.cast::<()>() as usize; } } @@ -53,6 +598,74 @@ pub(crate) fn aligned_to<T: AsAddress, U>(t: T) -> bool { remainder == 0 } +/// Round `n` down to the largest value `m` such that `m <= n` and `m % align == +/// 0`. +/// +/// # Panics +/// +/// May panic if `align` is not a power of two. Even if it doesn't panic in this +/// case, it will produce nonsense results. +#[inline(always)] +pub(crate) const fn round_down_to_next_multiple_of_alignment( + n: usize, + align: NonZeroUsize, +) -> usize { + let align = align.get(); + debug_assert!(align.is_power_of_two()); + + // Subtraction can't underflow because `align.get() >= 1`. + #[allow(clippy::arithmetic_side_effects)] + let mask = !(align - 1); + n & mask +} + +pub(crate) const fn max(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize { + if a.get() < b.get() { + b + } else { + a + } +} + +pub(crate) const fn min(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize { + if a.get() > b.get() { + b + } else { + a + } +} + +/// Since we support multiple versions of Rust, there are often features which +/// have been stabilized in the most recent stable release which do not yet +/// exist (stably) on our MSRV. This module provides polyfills for those +/// features so that we can write more "modern" code, and just remove the +/// polyfill once our MSRV supports the corresponding feature. Without this, +/// we'd have to write worse/more verbose code and leave TODO comments sprinkled +/// throughout the codebase to update to the new pattern once it's stabilized. +/// +/// Each trait is imported as `_` at the crate root; each polyfill should "just +/// work" at usage sites. +pub(crate) mod polyfills { + use core::ptr::{self, NonNull}; + + // A polyfill for `NonNull::slice_from_raw_parts` that we can use before our + // MSRV is 1.70, when that function was stabilized. + // + // TODO(#67): Once our MSRV is 1.70, remove this. + pub(crate) trait NonNullExt<T> { + fn slice_from_raw_parts(data: Self, len: usize) -> NonNull<[T]>; + } + + impl<T> NonNullExt<T> for NonNull<T> { + #[inline(always)] + fn slice_from_raw_parts(data: Self, len: usize) -> NonNull<[T]> { + let ptr = ptr::slice_from_raw_parts_mut(data.as_ptr(), len); + // SAFETY: `ptr` is converted from `data`, which is non-null. + unsafe { NonNull::new_unchecked(ptr) } + } + } +} + #[cfg(test)] pub(crate) mod testutil { use core::fmt::{self, Display, Formatter}; @@ -83,7 +696,18 @@ pub(crate) mod testutil { // Though `u64` has alignment 8 on some platforms, it's not guaranteed. // By contrast, `AU64` is guaranteed to have alignment 8. #[derive( - FromZeroes, FromBytes, AsBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone, + KnownLayout, + FromZeroes, + FromBytes, + AsBytes, + Eq, + PartialEq, + Ord, + PartialOrd, + Default, + Debug, + Copy, + Clone, )] #[repr(C, align(8))] pub(crate) struct AU64(pub(crate) u64); @@ -101,5 +725,84 @@ pub(crate) mod testutil { } } - impl_known_layout!(AU64); + #[derive( + FromZeroes, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone, + )] + #[repr(C)] + pub(crate) struct Nested<T, U: ?Sized> { + _t: T, + _u: U, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_round_down_to_next_multiple_of_alignment() { + fn alt_impl(n: usize, align: NonZeroUsize) -> usize { + let mul = n / align.get(); + mul * align.get() + } + + for align in [1, 2, 4, 8, 16] { + for n in 0..256 { + let align = NonZeroUsize::new(align).unwrap(); + let want = alt_impl(n, align); + let got = round_down_to_next_multiple_of_alignment(n, align); + assert_eq!(got, want, "round_down_to_next_multiple_of_alignment({n}, {align})"); + } + } + } +} + +#[cfg(kani)] +mod proofs { + use super::*; + + #[kani::proof] + fn prove_round_down_to_next_multiple_of_alignment() { + fn model_impl(n: usize, align: NonZeroUsize) -> usize { + assert!(align.get().is_power_of_two()); + let mul = n / align.get(); + mul * align.get() + } + + let align: NonZeroUsize = kani::any(); + kani::assume(align.get().is_power_of_two()); + let n: usize = kani::any(); + + let expected = model_impl(n, align); + let actual = round_down_to_next_multiple_of_alignment(n, align); + assert_eq!(expected, actual, "round_down_to_next_multiple_of_alignment({n}, {align})"); + } + + // Restricted to nightly since we use the unstable `usize::next_multiple_of` + // in our model implementation. + #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[kani::proof] + fn prove_padding_needed_for() { + fn model_impl(len: usize, align: NonZeroUsize) -> usize { + let padded = len.next_multiple_of(align.get()); + let padding = padded - len; + padding + } + + let align: NonZeroUsize = kani::any(); + kani::assume(align.get().is_power_of_two()); + let len: usize = kani::any(); + // Constrain `len` to valid Rust lengths, since our model implementation + // isn't robust to overflow. + kani::assume(len <= isize::MAX as usize); + kani::assume(align.get() < 1 << 29); + + let expected = model_impl(len, align); + let actual = core_layout::padding_needed_for(len, align); + assert_eq!(expected, actual, "padding_needed_for({len}, {align})"); + + let padded_len = actual + len; + assert_eq!(padded_len % align, 0); + assert!(padded_len / align >= len / align); + } } diff --git a/src/wrappers.rs b/src/wrappers.rs index a0e6ac7..532d872 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -1,6 +1,10 @@ -// Copyright 2023 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. use core::{ cmp::Ordering, @@ -54,10 +58,16 @@ use super::*; // [3] https://github.com/google/zerocopy/issues/209 #[allow(missing_debug_implementations)] #[derive(Default, Copy)] -#[cfg_attr(any(feature = "derive", test), derive(FromZeroes, FromBytes, AsBytes, Unaligned))] +#[cfg_attr( + any(feature = "derive", test), + derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned) +)] #[repr(C, packed)] pub struct Unalign<T>(T); +#[cfg(not(any(feature = "derive", test)))] +impl_known_layout!(T => Unalign<T>); + safety_comment! { /// SAFETY: /// - `Unalign<T>` is `repr(packed)`, so it is unaligned regardless of the @@ -268,17 +278,13 @@ impl<T> Unalign<T> { impl<T> Drop for WriteBackOnDrop<T> { fn drop(&mut self) { - // SAFETY: See inline comments. - unsafe { - // SAFETY: We never use `copy` again as required by - // `ManuallyDrop::take`. - let copy = ManuallyDrop::take(&mut self.copy); - // SAFETY: `slf` is the raw pointer value of `self`. We know - // it is valid for writes and properly aligned because - // `self` is a mutable reference, which guarantees both of - // these properties. - ptr::write(self.slf, Unalign::new(copy)); - } + // SAFETY: We never use `copy` again as required by + // `ManuallyDrop::take`. + let copy = unsafe { ManuallyDrop::take(&mut self.copy) }; + // SAFETY: `slf` is the raw pointer value of `self`. We know it + // is valid for writes and properly aligned because `self` is a + // mutable reference, which guarantees both of these properties. + unsafe { ptr::write(self.slf, Unalign::new(copy)) }; } } diff --git a/testdata/include_value/data b/testdata/include_value/data new file mode 100644 index 0000000..85df507 --- /dev/null +++ b/testdata/include_value/data @@ -0,0 +1 @@ +abcd
\ No newline at end of file diff --git a/tests/trybuild.rs b/tests/trybuild.rs index 4ed01f7..24abc28 100644 --- a/tests/trybuild.rs +++ b/tests/trybuild.rs @@ -1,33 +1,23 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2019 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. -// UI tests depend on the exact error messages emitted by rustc, but those error -// messages are not stable, and sometimes change between Rust versions. Thus, we -// maintain one set of UI tests for each Rust version that we test in CI, and we -// pin to specific versions in CI (a specific stable version, a specific date of -// the nightly compiler, and a specific MSRV). Updating those pinned versions -// may also require updating these tests. -// - `tests/ui-nightly` - Contains the source of truth for our UI test source -// files (`.rs`), and contains `.err` and `.out` files for nightly -// - `tests/ui-stable` - Contains symlinks to the `.rs` files in -// `tests/ui-nightly`, and contains `.err` and `.out` files for stable -// - `tests/ui-msrv` - Contains symlinks to the `.rs` files in -// `tests/ui-nightly`, and contains `.err` and `.out` files for MSRV - -#[rustversion::nightly] -const SOURCE_FILES_DIR: &str = "tests/ui-nightly"; -#[rustversion::stable(1.69.0)] -const SOURCE_FILES_DIR: &str = "tests/ui-stable"; -#[rustversion::stable(1.61.0)] -const SOURCE_FILES_DIR: &str = "tests/ui-msrv"; - -const SOURCE_FILES_DIR: &str = "tests/ui-stable"; +use testutil::ToolchainVersion; #[test] +#[cfg_attr(miri, ignore)] fn ui() { + let version = ToolchainVersion::extract_from_pwd().unwrap(); + // See the doc comment on this method for an explanation of what this does + // and why we store source files in different directories. + let source_files_dirname = version.get_ui_source_files_dirname_and_maybe_print_warning(); + let t = trybuild::TestCases::new(); - t.compile_fail(format!("{SOURCE_FILES_DIR}/*.rs")); + t.compile_fail(format!("tests/{source_files_dirname}/*.rs")); } // The file `invalid-impls.rs` directly includes `src/macros.rs` in order to @@ -39,7 +29,13 @@ fn ui() { // tests the correct behavior when the "derive" feature is enabled. #[cfg(feature = "derive")] #[test] +#[cfg_attr(miri, ignore)] fn ui_invalid_impls() { + let version = ToolchainVersion::extract_from_pwd().unwrap(); + // See the doc comment on this method for an explanation of what this does + // and why we store source files in different directories. + let source_files_dirname = version.get_ui_source_files_dirname_and_maybe_print_warning(); + let t = trybuild::TestCases::new(); - t.compile_fail(format!("{SOURCE_FILES_DIR}/invalid-impls/*.rs")); + t.compile_fail(format!("tests/{source_files_dirname}/invalid-impls/*.rs")); } diff --git a/tests/ui-msrv/include_value_not_from_bytes.rs b/tests/ui-msrv/include_value_not_from_bytes.rs new file mode 100644 index 0000000..45b6138 --- /dev/null +++ b/tests/ui-msrv/include_value_not_from_bytes.rs @@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[macro_use] +extern crate zerocopy; + +fn main() {} + +// Should fail because `UnsafeCell<u32>: !FromBytes`. +const NOT_FROM_BYTES: core::cell::UnsafeCell<u32> = + include_value!("../../testdata/include_value/data"); diff --git a/tests/ui-msrv/include_value_not_from_bytes.stderr b/tests/ui-msrv/include_value_not_from_bytes.stderr new file mode 100644 index 0000000..21f6443 --- /dev/null +++ b/tests/ui-msrv/include_value_not_from_bytes.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `UnsafeCell<u32>: FromBytes` is not satisfied + --> tests/ui-msrv/include_value_not_from_bytes.rs:12:5 + | +12 | include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `UnsafeCell<u32>` + | +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-msrv/include_value_not_from_bytes.rs:12:5 + | +12 | include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `$crate::transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-illegal.rs b/tests/ui-msrv/include_value_wrong_size.rs index 74b8439..d87b306 100644 --- a/tests/ui-stable/transmute-illegal.rs +++ b/tests/ui-msrv/include_value_wrong_size.rs @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#[macro_use] extern crate zerocopy; fn main() {} -// It is unsound to inspect the usize value of a pointer during const eval. -const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); +// Should fail because the file is 4 bytes long, not 8. +const WRONG_SIZE: u64 = include_value!("../../testdata/include_value/data"); diff --git a/tests/ui-msrv/include_value_wrong_size.stderr b/tests/ui-msrv/include_value_wrong_size.stderr new file mode 100644 index 0000000..3004584 --- /dev/null +++ b/tests/ui-msrv/include_value_wrong_size.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/include_value_wrong_size.rs:11:25 + | +11 | const WRONG_SIZE: u64 = include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 4]` (32 bits) + = note: target type: `u64` (64 bits) + = note: this error originates in the macro `$crate::transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/invalid-impls/invalid-impls.rs b/tests/ui-msrv/invalid-impls/invalid-impls.rs index b9a60bd..ea96390 100644 --- a/tests/ui-msrv/invalid-impls/invalid-impls.rs +++ b/tests/ui-msrv/invalid-impls/invalid-impls.rs @@ -1,6 +1,10 @@ -// Copyright 2022 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. // Since some macros from `macros.rs` are unused. #![allow(unused)] diff --git a/tests/ui-msrv/invalid-impls/invalid-impls.stderr b/tests/ui-msrv/invalid-impls/invalid-impls.stderr index fee0cd9..c1de466 100644 --- a/tests/ui-msrv/invalid-impls/invalid-impls.stderr +++ b/tests/ui-msrv/invalid-impls/invalid-impls.stderr @@ -4,15 +4,15 @@ error[E0277]: the trait bound `T: zerocopy::FromZeroes` is not satisfied | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {} | ^^^^^^^^ the trait `zerocopy::FromZeroes` is not implemented for `T` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:22:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:26:1 | -22 | impl_or_verify!(T => FromZeroes for Foo<T>); +26 | impl_or_verify!(T => FromZeroes for Foo<T>); | ------------------------------------------- in this macro invocation | note: required because of the requirements on the impl of `zerocopy::FromZeroes` for `Foo<T>` - --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:10 + --> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:10 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^^ note: required by a bound in `_::Subtrait` --> tests/ui-msrv/invalid-impls/../../../src/macros.rs @@ -20,15 +20,15 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `_::Subtrait` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:22:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:26:1 | -22 | impl_or_verify!(T => FromZeroes for Foo<T>); +26 | impl_or_verify!(T => FromZeroes for Foo<T>); | ------------------------------------------- in this macro invocation = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -22 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>); - | ++++++++++++++++++++++ + | +26 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>); + | ++++++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied --> tests/ui-msrv/invalid-impls/../../../src/macros.rs @@ -36,15 +36,15 @@ error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {} | ^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `T` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:23:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:27:1 | -23 | impl_or_verify!(T => FromBytes for Foo<T>); +27 | impl_or_verify!(T => FromBytes for Foo<T>); | ------------------------------------------ in this macro invocation | note: required because of the requirements on the impl of `zerocopy::FromBytes` for `Foo<T>` - --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:22 + --> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:22 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^ note: required by a bound in `_::Subtrait` --> tests/ui-msrv/invalid-impls/../../../src/macros.rs @@ -52,15 +52,15 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `_::Subtrait` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:23:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:27:1 | -23 | impl_or_verify!(T => FromBytes for Foo<T>); +27 | impl_or_verify!(T => FromBytes for Foo<T>); | ------------------------------------------ in this macro invocation = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -23 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>); - | +++++++++++++++++++++ + | +27 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>); + | +++++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied --> tests/ui-msrv/invalid-impls/../../../src/macros.rs @@ -68,15 +68,15 @@ error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {} | ^^^^^^^^ the trait `zerocopy::AsBytes` is not implemented for `T` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:24:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:28:1 | -24 | impl_or_verify!(T => AsBytes for Foo<T>); +28 | impl_or_verify!(T => AsBytes for Foo<T>); | ---------------------------------------- in this macro invocation | note: required because of the requirements on the impl of `zerocopy::AsBytes` for `Foo<T>` - --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:33 + --> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:33 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^ note: required by a bound in `_::Subtrait` --> tests/ui-msrv/invalid-impls/../../../src/macros.rs @@ -84,15 +84,15 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `_::Subtrait` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:24:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:28:1 | -24 | impl_or_verify!(T => AsBytes for Foo<T>); +28 | impl_or_verify!(T => AsBytes for Foo<T>); | ---------------------------------------- in this macro invocation = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -24 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>); - | +++++++++++++++++++ + | +28 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>); + | +++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied --> tests/ui-msrv/invalid-impls/../../../src/macros.rs @@ -100,15 +100,15 @@ error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied | impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {} | ^^^^^^^^ the trait `zerocopy::Unaligned` is not implemented for `T` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:25:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:29:1 | -25 | impl_or_verify!(T => Unaligned for Foo<T>); +29 | impl_or_verify!(T => Unaligned for Foo<T>); | ------------------------------------------ in this macro invocation | note: required because of the requirements on the impl of `zerocopy::Unaligned` for `Foo<T>` - --> tests/ui-msrv/invalid-impls/invalid-impls.rs:18:42 + --> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:42 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^ note: required by a bound in `_::Subtrait` --> tests/ui-msrv/invalid-impls/../../../src/macros.rs @@ -116,12 +116,12 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `_::Subtrait` | - ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:25:1 + ::: tests/ui-msrv/invalid-impls/invalid-impls.rs:29:1 | -25 | impl_or_verify!(T => Unaligned for Foo<T>); +29 | impl_or_verify!(T => Unaligned for Foo<T>); | ------------------------------------------ in this macro invocation = note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -25 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>); - | +++++++++++++++++++++ + | +29 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>); + | +++++++++++++++++++++ diff --git a/tests/ui-msrv/max-align.rs b/tests/ui-msrv/max-align.rs new file mode 100644 index 0000000..53e3eb9 --- /dev/null +++ b/tests/ui-msrv/max-align.rs @@ -0,0 +1,99 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +#[repr(C, align(1))] +struct Align1; + +#[repr(C, align(2))] +struct Align2; + +#[repr(C, align(4))] +struct Align4; + +#[repr(C, align(8))] +struct Align8; + +#[repr(C, align(16))] +struct Align16; + +#[repr(C, align(32))] +struct Align32; + +#[repr(C, align(64))] +struct Align64; + +#[repr(C, align(128))] +struct Align128; + +#[repr(C, align(256))] +struct Align256; + +#[repr(C, align(512))] +struct Align512; + +#[repr(C, align(1024))] +struct Align1024; + +#[repr(C, align(2048))] +struct Align2048; + +#[repr(C, align(4096))] +struct Align4096; + +#[repr(C, align(8192))] +struct Align8192; + +#[repr(C, align(16384))] +struct Align16384; + +#[repr(C, align(32768))] +struct Align32768; + +#[repr(C, align(65536))] +struct Align65536; + +#[repr(C, align(131072))] +struct Align131072; + +#[repr(C, align(262144))] +struct Align262144; + +#[repr(C, align(524288))] +struct Align524288; + +#[repr(C, align(1048576))] +struct Align1048576; + +#[repr(C, align(2097152))] +struct Align2097152; + +#[repr(C, align(4194304))] +struct Align4194304; + +#[repr(C, align(8388608))] +struct Align8388608; + +#[repr(C, align(16777216))] +struct Align16777216; + +#[repr(C, align(33554432))] +struct Align33554432; + +#[repr(C, align(67108864))] +struct Align67108864; + +#[repr(C, align(134217728))] +struct Align13421772; + +#[repr(C, align(268435456))] +struct Align26843545; + +#[repr(C, align(1073741824))] +struct Align1073741824; + +fn main() {} diff --git a/tests/ui-msrv/max-align.stderr b/tests/ui-msrv/max-align.stderr new file mode 100644 index 0000000..6ab6e47 --- /dev/null +++ b/tests/ui-msrv/max-align.stderr @@ -0,0 +1,5 @@ +error[E0589]: invalid `repr(align)` attribute: larger than 2^29 + --> tests/ui-msrv/max-align.rs:96:11 + | +96 | #[repr(C, align(1073741824))] + | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-msrv/transmute-dst-not-frombytes.rs b/tests/ui-msrv/transmute-dst-not-frombytes.rs new file mode 100644 index 0000000..c4caaff --- /dev/null +++ b/tests/ui-msrv/transmute-dst-not-frombytes.rs @@ -0,0 +1,18 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); diff --git a/tests/ui-msrv/transmute-dst-not-frombytes.stderr b/tests/ui-msrv/transmute-dst-not-frombytes.stderr new file mode 100644 index 0000000..b4afbbd --- /dev/null +++ b/tests/ui-msrv/transmute-dst-not-frombytes.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `NotZerocopy: FromBytes` is not satisfied + --> tests/ui-msrv/transmute-dst-not-frombytes.rs:18:41 + | +18 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `NotZerocopy` + | +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-msrv/transmute-dst-not-frombytes.rs:18:41 + | +18 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-illegal.stderr b/tests/ui-msrv/transmute-illegal.stderr deleted file mode 100644 index 37c124a..0000000 --- a/tests/ui-msrv/transmute-illegal.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied - --> tests/ui-msrv/transmute-illegal.rs:10:30 - | -10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `*const usize` - | - = help: the following implementations were found: - <usize as AsBytes> - <f32 as AsBytes> - <f64 as AsBytes> - <i128 as AsBytes> - and $N others -note: required by a bound in `POINTER_VALUE::transmute` - --> tests/ui-msrv/transmute-illegal.rs:10:30 - | -10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `POINTER_VALUE::transmute` - = note: this error originates in the macro `zerocopy::transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-alignment-increase.rs b/tests/ui-msrv/transmute-mut-alignment-increase.rs new file mode 100644 index 0000000..0928564 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-alignment-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a type of smaller +// alignment to one of larger alignment. +const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); diff --git a/tests/ui-msrv/transmute-mut-alignment-increase.stderr b/tests/ui-msrv/transmute-mut-alignment-increase.stderr new file mode 100644 index 0000000..033031c --- /dev/null +++ b/tests/ui-msrv/transmute-mut-alignment-increase.stderr @@ -0,0 +1,36 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-mut-alignment-increase.rs:19:39 + | +19 | const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<[u8; 2]>` (8 bits) + = note: target type: `MaxAlignsOf<[u8; 2], AU16>` (16 bits) + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: mutable references are not allowed in constants + --> tests/ui-msrv/transmute-mut-alignment-increase.rs:19:54 + | +19 | const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^ + | + = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information + +error[E0015]: cannot call non-const fn `transmute_mut::<[u8; 2], AU16>` in constants + --> tests/ui-msrv/transmute-mut-alignment-increase.rs:19:39 + | +19 | const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> tests/ui-msrv/transmute-mut-alignment-increase.rs:19:59 + | +19 | const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); + | --------------------^^^^^^^^- + | | | + | | creates a temporary which is freed while still in use + | temporary value is freed at the end of this statement + | using this value as a constant requires that borrow lasts for `'static` diff --git a/tests/ui-msrv/transmute-mut-const.rs b/tests/ui-msrv/transmute-mut-const.rs new file mode 100644 index 0000000..021b562 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-const.rs @@ -0,0 +1,20 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +const ARRAY_OF_U8S: [u8; 2] = [0u8; 2]; + +// `transmute_mut!` cannot, generally speaking, be used in const contexts. +const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); diff --git a/tests/ui-msrv/transmute-mut-const.stderr b/tests/ui-msrv/transmute-mut-const.stderr new file mode 100644 index 0000000..30bfe45 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-const.stderr @@ -0,0 +1,41 @@ +warning: taking a mutable reference to a `const` item + --> tests/ui-msrv/transmute-mut-const.rs:20:52 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(const_item_mutation)]` on by default + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> tests/ui-msrv/transmute-mut-const.rs:17:1 + | +17 | const ARRAY_OF_U8S: [u8; 2] = [0u8; 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0658]: mutable references are not allowed in constants + --> tests/ui-msrv/transmute-mut-const.rs:20:52 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information + +error[E0015]: cannot call non-const fn `transmute_mut::<[u8; 2], [u8; 2]>` in constants + --> tests/ui-msrv/transmute-mut-const.rs:20:37 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> tests/ui-msrv/transmute-mut-const.rs:20:57 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | --------------------^^^^^^^^^^^^- + | | | + | | creates a temporary which is freed while still in use + | temporary value is freed at the end of this statement + | using this value as a constant requires that borrow lasts for `'static` diff --git a/tests/ui-msrv/transmute-mut-dst-generic.rs b/tests/ui-msrv/transmute-mut-dst-generic.rs new file mode 100644 index 0000000..7068f10 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes>(u: &mut u8) -> &mut T { + // `transmute_mut!` requires the destination type to be concrete. + transmute_mut!(u) +} diff --git a/tests/ui-msrv/transmute-mut-dst-generic.stderr b/tests/ui-msrv/transmute-mut-dst-generic.stderr new file mode 100644 index 0000000..f6b54ce --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-mut-dst-generic.rs:17:5 + | +17 | transmute_mut!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `T` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-mut-dst-generic.rs:17:5 + | +17 | transmute_mut!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<u8>` (8 bits) + = note: target type: `MaxAlignsOf<u8, T>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-dst-not-a-reference.rs b/tests/ui-msrv/transmute-mut-dst-not-a-reference.rs new file mode 100644 index 0000000..33a9ecd --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting into a non-reference +// destination type. +const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); diff --git a/tests/ui-msrv/transmute-mut-dst-not-a-reference.stderr b/tests/ui-msrv/transmute-mut-dst-not-a-reference.stderr new file mode 100644 index 0000000..8f0ea80 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-not-a-reference.stderr @@ -0,0 +1,39 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-dst-not-asbytes.rs b/tests/ui-msrv/transmute-mut-dst-not-asbytes.rs new file mode 100644 index 0000000..b72f129 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-not-asbytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the destination type implements `AsBytes` +const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-msrv/transmute-mut-dst-not-asbytes.stderr b/tests/ui-msrv/transmute-mut-dst-not-asbytes.stderr new file mode 100644 index 0000000..7e2dd78 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-not-asbytes.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `Dst: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-mut-dst-not-asbytes.rs:24:36 + | +24 | const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `Dst` + | +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-msrv/transmute-mut-dst-not-asbytes.rs:24:36 + | +24 | const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-dst-not-frombytes.rs b/tests/ui-msrv/transmute-mut-dst-not-frombytes.rs new file mode 100644 index 0000000..102fced --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-not-frombytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-msrv/transmute-mut-dst-not-frombytes.stderr b/tests/ui-msrv/transmute-mut-dst-not-frombytes.stderr new file mode 100644 index 0000000..663e085 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-not-frombytes.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `Dst: FromBytes` is not satisfied + --> tests/ui-msrv/transmute-mut-dst-not-frombytes.rs:24:38 + | +24 | const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `Dst` + | +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-msrv/transmute-mut-dst-not-frombytes.rs:24:38 + | +24 | const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-dst-unsized.rs b/tests/ui-msrv/transmute-mut-dst-unsized.rs new file mode 100644 index 0000000..693ccda --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting into an unsized destination +// type. +const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); diff --git a/tests/ui-msrv/transmute-mut-dst-unsized.stderr b/tests/ui-msrv/transmute-mut-dst-unsized.stderr new file mode 100644 index 0000000..cb60a82 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-dst-unsized.stderr @@ -0,0 +1,108 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<T, U>(e: T) -> U; + | ^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-illegal-lifetime.rs b/tests/ui-msrv/transmute-mut-illegal-lifetime.rs new file mode 100644 index 0000000..c31765e --- /dev/null +++ b/tests/ui-msrv/transmute-mut-illegal-lifetime.rs @@ -0,0 +1,15 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +fn main() {} + +fn increase_lifetime() { + let mut x = 0u64; + // It is illegal to increase the lifetime scope. + let _: &'static mut u64 = zerocopy::transmute_mut!(&mut x); +} diff --git a/tests/ui-msrv/transmute-mut-illegal-lifetime.stderr b/tests/ui-msrv/transmute-mut-illegal-lifetime.stderr new file mode 100644 index 0000000..5ff7145 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-illegal-lifetime.stderr @@ -0,0 +1,9 @@ +error[E0597]: `x` does not live long enough + --> tests/ui-msrv/transmute-mut-illegal-lifetime.rs:14:56 + | +14 | let _: &'static mut u64 = zerocopy::transmute_mut!(&mut x); + | ---------------- ^^^^^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +15 | } + | - `x` dropped here while still borrowed diff --git a/tests/ui-msrv/transmute-mut-size-decrease.rs b/tests/ui-msrv/transmute-mut-size-decrease.rs new file mode 100644 index 0000000..c6eec3a --- /dev/null +++ b/tests/ui-msrv/transmute-mut-size-decrease.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// We require that the size of the destination type is not smaller than the size +// of the source type. +const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); diff --git a/tests/ui-msrv/transmute-mut-size-decrease.stderr b/tests/ui-msrv/transmute-mut-size-decrease.stderr new file mode 100644 index 0000000..2bfc218 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-size-decrease.stderr @@ -0,0 +1,36 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-mut-size-decrease.rs:17:32 + | +17 | const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 2]` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: mutable references are not allowed in constants + --> tests/ui-msrv/transmute-mut-size-decrease.rs:17:47 + | +17 | const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^ + | + = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information + +error[E0015]: cannot call non-const fn `transmute_mut::<[u8; 2], u8>` in constants + --> tests/ui-msrv/transmute-mut-size-decrease.rs:17:32 + | +17 | const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> tests/ui-msrv/transmute-mut-size-decrease.rs:17:52 + | +17 | const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); + | --------------------^^^^^^^^- + | | | + | | creates a temporary which is freed while still in use + | temporary value is freed at the end of this statement + | using this value as a constant requires that borrow lasts for `'static` diff --git a/tests/ui-msrv/transmute-mut-size-increase.rs b/tests/ui-msrv/transmute-mut-size-increase.rs new file mode 100644 index 0000000..a4657c2 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-size-increase.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); diff --git a/tests/ui-msrv/transmute-mut-size-increase.stderr b/tests/ui-msrv/transmute-mut-size-increase.stderr new file mode 100644 index 0000000..6e866a0 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-size-increase.stderr @@ -0,0 +1,36 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-mut-size-increase.rs:17:37 + | +17 | const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `[u8; 2]` (16 bits) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: mutable references are not allowed in constants + --> tests/ui-msrv/transmute-mut-size-increase.rs:17:52 + | +17 | const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); + | ^^^^^^^^ + | + = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information + +error[E0015]: cannot call non-const fn `transmute_mut::<u8, [u8; 2]>` in constants + --> tests/ui-msrv/transmute-mut-size-increase.rs:17:37 + | +17 | const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> tests/ui-msrv/transmute-mut-size-increase.rs:17:57 + | +17 | const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); + | --------------------^^^- + | | | + | | creates a temporary which is freed while still in use + | temporary value is freed at the end of this statement + | using this value as a constant requires that borrow lasts for `'static` diff --git a/tests/ui-msrv/transmute-mut-src-dst-generic.rs b/tests/ui-msrv/transmute-mut-src-dst-generic.rs new file mode 100644 index 0000000..aed7ded --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-dst-generic.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes, U: AsBytes + FromBytes>(t: &mut T) -> &mut U { + // `transmute_mut!` requires the source and destination types to be + // concrete. + transmute_mut!(t) +} diff --git a/tests/ui-msrv/transmute-mut-src-dst-generic.stderr b/tests/ui-msrv/transmute-mut-src-dst-generic.stderr new file mode 100644 index 0000000..1162f21 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-mut-src-dst-generic.rs:18:5 + | +18 | transmute_mut!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `U` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-mut-src-dst-generic.rs:18:5 + | +18 | transmute_mut!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, U>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-src-dst-not-references.rs b/tests/ui-msrv/transmute-mut-src-dst-not-references.rs new file mode 100644 index 0000000..98cc520 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-dst-not-references.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting between non-reference source +// and destination types. +const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(0usize); diff --git a/tests/ui-msrv/transmute-mut-src-dst-not-references.stderr b/tests/ui-msrv/transmute-mut-src-dst-not-references.stderr new file mode 100644 index 0000000..c500a93 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-dst-not-references.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-mut-src-dst-not-references.rs:17:59 + | +17 | const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&mut _`, found `usize` + | | help: consider mutably borrowing here: `&mut 0usize` + | expected due to this + | + = note: expected mutable reference `&mut _` + found type `usize` diff --git a/tests/ui-msrv/transmute-mut-src-dst-unsized.rs b/tests/ui-msrv/transmute-mut-src-dst-unsized.rs new file mode 100644 index 0000000..1bebcf2 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting between unsized source and +// destination types. +const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); diff --git a/tests/ui-msrv/transmute-mut-src-dst-unsized.stderr b/tests/ui-msrv/transmute-mut-src-dst-unsized.stderr new file mode 100644 index 0000000..00201a6 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-dst-unsized.stderr @@ -0,0 +1,237 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<T, U>(e: T) -> U; + | ^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all function arguments must have a statically known size + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-src-generic.rs b/tests/ui-msrv/transmute-mut-src-generic.rs new file mode 100644 index 0000000..a3ef397 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes>(t: &mut T) -> &mut u8 { + // `transmute_mut!` requires the source type to be concrete. + transmute_mut!(t) +} diff --git a/tests/ui-msrv/transmute-mut-src-generic.stderr b/tests/ui-msrv/transmute-mut-src-generic.stderr new file mode 100644 index 0000000..8a9296c --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-generic.stderr @@ -0,0 +1,10 @@ +error[E0405]: cannot find trait `FromBytes` in this scope + --> tests/ui-msrv/transmute-mut-src-generic.rs:15:31 + | +15 | fn transmute_mut<T: AsBytes + FromBytes>(t: &mut T) -> &mut u8 { + | ^^^^^^^^^ not found in this scope + | +help: consider importing this trait + | +11 | use zerocopy::FromBytes; + | diff --git a/tests/ui-msrv/transmute-mut-src-immutable.rs b/tests/ui-msrv/transmute-mut-src-immutable.rs new file mode 100644 index 0000000..08088d0 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-immutable.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +fn ref_src_immutable() { + // `transmute_mut!` requires that its source type be a mutable reference. + let _: &mut u8 = transmute_mut!(&0u8); +} diff --git a/tests/ui-msrv/transmute-mut-src-immutable.stderr b/tests/ui-msrv/transmute-mut-src-immutable.stderr new file mode 100644 index 0000000..8262f16 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-immutable.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-mut-src-immutable.rs:17:37 + | +17 | let _: &mut u8 = transmute_mut!(&0u8); + | ---------------^^^^- + | | | + | | types differ in mutability + | expected due to this + | + = note: expected mutable reference `&mut _` + found reference `&u8` diff --git a/tests/ui-msrv/transmute-mut-src-not-a-reference.rs b/tests/ui-msrv/transmute-mut-src-not-a-reference.rs new file mode 100644 index 0000000..bf8bc32 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a non-reference source +// type. +const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(0usize); diff --git a/tests/ui-msrv/transmute-mut-src-not-a-reference.stderr b/tests/ui-msrv/transmute-mut-src-not-a-reference.stderr new file mode 100644 index 0000000..3a6bdf7 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-not-a-reference.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-mut-src-not-a-reference.rs:17:53 + | +17 | const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&mut _`, found `usize` + | | help: consider mutably borrowing here: `&mut 0usize` + | expected due to this + | + = note: expected mutable reference `&mut _` + found type `usize` diff --git a/tests/ui-msrv/transmute-mut-src-not-asbytes.rs b/tests/ui-msrv/transmute-mut-src-not-asbytes.rs new file mode 100644 index 0000000..6a14f12 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-not-asbytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-msrv/transmute-mut-src-not-asbytes.stderr b/tests/ui-msrv/transmute-mut-src-not-asbytes.stderr new file mode 100644 index 0000000..4056975 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-not-asbytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `Src: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `Src` + | +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-msrv/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Src: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `Src` + | +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-msrv/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-src-not-frombytes.rs b/tests/ui-msrv/transmute-mut-src-not-frombytes.rs new file mode 100644 index 0000000..2ebe036 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-not-frombytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the source type implements `FromBytes` +const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-msrv/transmute-mut-src-not-frombytes.stderr b/tests/ui-msrv/transmute-mut-src-not-frombytes.stderr new file mode 100644 index 0000000..b859c41 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-not-frombytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `Src: FromBytes` is not satisfied + --> tests/ui-msrv/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `Src` + | +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-msrv/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Src: FromBytes` is not satisfied + --> tests/ui-msrv/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `Src` + | +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-msrv/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-mut-src-unsized.rs b/tests/ui-msrv/transmute-mut-src-unsized.rs new file mode 100644 index 0000000..413dd68 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-unsized.rs @@ -0,0 +1,16 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from an unsized source type. +const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); diff --git a/tests/ui-msrv/transmute-mut-src-unsized.stderr b/tests/ui-msrv/transmute-mut-src-unsized.stderr new file mode 100644 index 0000000..6b18695 --- /dev/null +++ b/tests/ui-msrv/transmute-mut-src-unsized.stderr @@ -0,0 +1,198 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<T, U>(e: T) -> U; + | ^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all function arguments must have a statically known size + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ptr-to-usize.rs b/tests/ui-msrv/transmute-ptr-to-usize.rs new file mode 100644 index 0000000..5af8859 --- /dev/null +++ b/tests/ui-msrv/transmute-ptr-to-usize.rs @@ -0,0 +1,20 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// It is unclear whether we can or should support this transmutation, especially +// in a const context. This test ensures that even if such a transmutation +// becomes valid due to the requisite implementations of `FromBytes` being +// added, that we re-examine whether it should specifically be valid in a const +// context. +const POINTER_VALUE: usize = transmute!(&0usize as *const usize); diff --git a/tests/ui-msrv/transmute-ptr-to-usize.stderr b/tests/ui-msrv/transmute-ptr-to-usize.stderr new file mode 100644 index 0000000..06b1bba --- /dev/null +++ b/tests/ui-msrv/transmute-ptr-to-usize.stderr @@ -0,0 +1,37 @@ +error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `*const usize` + | + = help: the following implementations were found: + <usize as AsBytes> + <f32 as AsBytes> + <f64 as AsBytes> + <i128 as AsBytes> + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `*const usize` + | + = help: the following implementations were found: + <usize as AsBytes> + <f32 as AsBytes> + <f64 as AsBytes> + <i128 as AsBytes> + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-alignment-increase.rs b/tests/ui-msrv/transmute-ref-alignment-increase.rs new file mode 100644 index 0000000..bf1988c --- /dev/null +++ b/tests/ui-msrv/transmute-ref-alignment-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a type of smaller +// alignment to one of larger alignment. +const INCREASE_ALIGNMENT: &AU16 = transmute_ref!(&[0u8; 2]); diff --git a/tests/ui-msrv/transmute-ref-alignment-increase.stderr b/tests/ui-msrv/transmute-ref-alignment-increase.stderr new file mode 100644 index 0000000..72864e1 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-alignment-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-alignment-increase.rs:19:35 + | +19 | const INCREASE_ALIGNMENT: &AU16 = transmute_ref!(&[0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<[u8; 2]>` (8 bits) + = note: target type: `MaxAlignsOf<[u8; 2], AU16>` (16 bits) + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-dst-generic.rs b/tests/ui-msrv/transmute-ref-dst-generic.rs new file mode 100644 index 0000000..bf4a0f9 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, FromBytes}; + +fn main() {} + +fn transmute_ref<T: FromBytes>(u: &u8) -> &T { + // `transmute_ref!` requires the destination type to be concrete. + transmute_ref!(u) +} diff --git a/tests/ui-msrv/transmute-ref-dst-generic.stderr b/tests/ui-msrv/transmute-ref-dst-generic.stderr new file mode 100644 index 0000000..ec7ec74 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-dst-generic.rs:17:5 + | +17 | transmute_ref!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `T` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-dst-generic.rs:17:5 + | +17 | transmute_ref!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<u8>` (8 bits) + = note: target type: `MaxAlignsOf<u8, T>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-dst-mutable.rs b/tests/ui-msrv/transmute-ref-dst-mutable.rs new file mode 100644 index 0000000..fa0e6e4 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-mutable.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +fn ref_dst_mutable() { + // `transmute_ref!` requires that its destination type be an immutable + // reference. + let _: &mut u8 = transmute_ref!(&0u8); +} diff --git a/tests/ui-msrv/transmute-ref-dst-mutable.stderr b/tests/ui-msrv/transmute-ref-dst-mutable.stderr new file mode 100644 index 0000000..5ccf2cd --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-mutable.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-dst-not-a-reference.rs b/tests/ui-msrv/transmute-ref-dst-not-a-reference.rs new file mode 100644 index 0000000..de55f9a --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting into a non-reference +// destination type. +const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); diff --git a/tests/ui-msrv/transmute-ref-dst-not-a-reference.stderr b/tests/ui-msrv/transmute-ref-dst-not-a-reference.stderr new file mode 100644 index 0000000..9a61c4c --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-not-a-reference.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found reference + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found reference + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found reference + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-dst-not-frombytes.rs b/tests/ui-msrv/transmute-ref-dst-not-frombytes.rs new file mode 100644 index 0000000..d81f64d --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-not-frombytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); diff --git a/tests/ui-msrv/transmute-ref-dst-not-frombytes.stderr b/tests/ui-msrv/transmute-ref-dst-not-frombytes.stderr new file mode 100644 index 0000000..d317675 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-not-frombytes.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `NotZerocopy: FromBytes` is not satisfied + --> tests/ui-msrv/transmute-ref-dst-not-frombytes.rs:18:42 + | +18 | const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `NotZerocopy` + | +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-msrv/transmute-ref-dst-not-frombytes.rs:18:42 + | +18 | const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-dst-unsized.rs b/tests/ui-msrv/transmute-ref-dst-unsized.rs new file mode 100644 index 0000000..625f1fa --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting into an unsized destination +// type. +const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); diff --git a/tests/ui-msrv/transmute-ref-dst-unsized.stderr b/tests/ui-msrv/transmute-ref-dst-unsized.stderr new file mode 100644 index 0000000..78135de --- /dev/null +++ b/tests/ui-msrv/transmute-ref-dst-unsized.stderr @@ -0,0 +1,94 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<T, U>(e: T) -> U; + | ^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-illegal-lifetime.rs b/tests/ui-msrv/transmute-ref-illegal-lifetime.rs new file mode 100644 index 0000000..8dd191e --- /dev/null +++ b/tests/ui-msrv/transmute-ref-illegal-lifetime.rs @@ -0,0 +1,15 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +fn main() {} + +fn increase_lifetime() { + let x = 0u64; + // It is illegal to increase the lifetime scope. + let _: &'static u64 = zerocopy::transmute_ref!(&x); +} diff --git a/tests/ui-msrv/transmute-ref-illegal-lifetime.stderr b/tests/ui-msrv/transmute-ref-illegal-lifetime.stderr new file mode 100644 index 0000000..866ea56 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-illegal-lifetime.stderr @@ -0,0 +1,9 @@ +error[E0597]: `x` does not live long enough + --> tests/ui-msrv/transmute-ref-illegal-lifetime.rs:14:52 + | +14 | let _: &'static u64 = zerocopy::transmute_ref!(&x); + | ------------ ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +15 | } + | - `x` dropped here while still borrowed diff --git a/tests/ui-msrv/transmute-ref-size-decrease.rs b/tests/ui-msrv/transmute-ref-size-decrease.rs new file mode 100644 index 0000000..1d66a54 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-size-decrease.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// Although this is not a soundness requirement, we currently require that the +// size of the destination type is not smaller than the size of the source type. +const DECREASE_SIZE: &u8 = transmute_ref!(&[0u8; 2]); diff --git a/tests/ui-msrv/transmute-ref-size-decrease.stderr b/tests/ui-msrv/transmute-ref-size-decrease.stderr new file mode 100644 index 0000000..95669f9 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-size-decrease.rs:17:28 + | +17 | const DECREASE_SIZE: &u8 = transmute_ref!(&[0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 2]` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-size-increase.rs b/tests/ui-msrv/transmute-ref-size-increase.rs new file mode 100644 index 0000000..cdca560 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-size-increase.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: &[u8; 2] = transmute_ref!(&0u8); diff --git a/tests/ui-msrv/transmute-ref-size-increase.stderr b/tests/ui-msrv/transmute-ref-size-increase.stderr new file mode 100644 index 0000000..10f0e10 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-size-increase.rs:17:33 + | +17 | const INCREASE_SIZE: &[u8; 2] = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `[u8; 2]` (16 bits) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-src-dst-generic.rs b/tests/ui-msrv/transmute-ref-src-dst-generic.rs new file mode 100644 index 0000000..409d785 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-dst-generic.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_ref<T: AsBytes, U: FromBytes>(t: &T) -> &U { + // `transmute_ref!` requires the source and destination types to be + // concrete. + transmute_ref!(t) +} diff --git a/tests/ui-msrv/transmute-ref-src-dst-generic.stderr b/tests/ui-msrv/transmute-ref-src-dst-generic.stderr new file mode 100644 index 0000000..eb3268f --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-src-dst-generic.rs:18:5 + | +18 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `U` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-src-dst-generic.rs:18:5 + | +18 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, U>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-src-dst-not-references.rs b/tests/ui-msrv/transmute-ref-src-dst-not-references.rs new file mode 100644 index 0000000..114e917 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-dst-not-references.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting between non-reference source +// and destination types. +const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); diff --git a/tests/ui-msrv/transmute-ref-src-dst-not-references.stderr b/tests/ui-msrv/transmute-ref-src-dst-not-references.stderr new file mode 100644 index 0000000..2c5e23b --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-dst-not-references.stderr @@ -0,0 +1,42 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-src-dst-not-references.rs:17:54 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ---------------^^^^^^- + | | | + | | expected reference, found `usize` + | | help: consider borrowing here: `&0usize` + | expected due to this + | + = note: expected reference `&_` + found type `usize` + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found reference + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found reference + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found reference + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-src-dst-unsized.rs b/tests/ui-msrv/transmute-ref-src-dst-unsized.rs new file mode 100644 index 0000000..6bfe7ff --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting between unsized source and +// destination types. +const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); diff --git a/tests/ui-msrv/transmute-ref-src-dst-unsized.stderr b/tests/ui-msrv/transmute-ref-src-dst-unsized.stderr new file mode 100644 index 0000000..adfd597 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-dst-unsized.stderr @@ -0,0 +1,195 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<T, U>(e: T) -> U; + | ^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all function arguments must have a statically known size + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-src-generic.rs b/tests/ui-msrv/transmute-ref-src-generic.rs new file mode 100644 index 0000000..010281c --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, AsBytes}; + +fn main() {} + +fn transmute_ref<T: AsBytes>(t: &T) -> &u8 { + // `transmute_ref!` requires the source type to be concrete. + transmute_ref!(t) +} diff --git a/tests/ui-msrv/transmute-ref-src-generic.stderr b/tests/ui-msrv/transmute-ref-src-generic.stderr new file mode 100644 index 0000000..4cb3e51 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-src-generic.rs:17:5 + | +17 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-ref-src-generic.rs:17:5 + | +17 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, u8>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-src-not-a-reference.rs b/tests/ui-msrv/transmute-ref-src-not-a-reference.rs new file mode 100644 index 0000000..90661b3 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a non-reference source +// type. +const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(0usize); diff --git a/tests/ui-msrv/transmute-ref-src-not-a-reference.stderr b/tests/ui-msrv/transmute-ref-src-not-a-reference.stderr new file mode 100644 index 0000000..0f4aeec --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-not-a-reference.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> tests/ui-msrv/transmute-ref-src-not-a-reference.rs:17:49 + | +17 | const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(0usize); + | ---------------^^^^^^- + | | | + | | expected reference, found `usize` + | | help: consider borrowing here: `&0usize` + | expected due to this + | + = note: expected reference `&_` + found type `usize` diff --git a/tests/ui-msrv/transmute-ref-src-not-asbytes.rs b/tests/ui-msrv/transmute-ref-src-not-asbytes.rs new file mode 100644 index 0000000..6ab19f3 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-not-asbytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); diff --git a/tests/ui-msrv/transmute-ref-src-not-asbytes.stderr b/tests/ui-msrv/transmute-ref-src-not-asbytes.stderr new file mode 100644 index 0000000..6b80d4f --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-not-asbytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ref-src-unsized.rs b/tests/ui-msrv/transmute-ref-src-unsized.rs new file mode 100644 index 0000000..14e72b4 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-unsized.rs @@ -0,0 +1,16 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from an unsized source type. +const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); diff --git a/tests/ui-msrv/transmute-ref-src-unsized.stderr b/tests/ui-msrv/transmute-ref-src-unsized.stderr new file mode 100644 index 0000000..43bac53 --- /dev/null +++ b/tests/ui-msrv/transmute-ref-src-unsized.stderr @@ -0,0 +1,170 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<T, U>(e: T) -> U; + | ^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-msrv/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all function arguments must have a statically known size + = note: this error originates in the macro `$crate::assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-size-decrease.rs b/tests/ui-msrv/transmute-size-decrease.rs new file mode 100644 index 0000000..1d56831 --- /dev/null +++ b/tests/ui-msrv/transmute-size-decrease.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// Although this is not a soundness requirement, we currently require that the +// size of the destination type is not smaller than the size of the source type. +const DECREASE_SIZE: u8 = transmute!(AU16(0)); diff --git a/tests/ui-msrv/transmute-size-decrease.stderr b/tests/ui-msrv/transmute-size-decrease.stderr new file mode 100644 index 0000000..ffa5688 --- /dev/null +++ b/tests/ui-msrv/transmute-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-size-decrease.rs:19:27 + | +19 | const DECREASE_SIZE: u8 = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AU16` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-size-increase.rs b/tests/ui-msrv/transmute-size-increase.rs new file mode 100644 index 0000000..32f9363 --- /dev/null +++ b/tests/ui-msrv/transmute-size-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: AU16 = transmute!(0u8); diff --git a/tests/ui-msrv/transmute-size-increase.stderr b/tests/ui-msrv/transmute-size-increase.stderr new file mode 100644 index 0000000..865d0ca --- /dev/null +++ b/tests/ui-msrv/transmute-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-size-increase.rs:19:29 + | +19 | const INCREASE_SIZE: AU16 = transmute!(0u8); + | ^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `AU16` (16 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-src-not-asbytes.rs b/tests/ui-msrv/transmute-src-not-asbytes.rs new file mode 100644 index 0000000..dd73021 --- /dev/null +++ b/tests/ui-msrv/transmute-src-not-asbytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); diff --git a/tests/ui-msrv/transmute-src-not-asbytes.stderr b/tests/ui-msrv/transmute-src-not-asbytes.stderr new file mode 100644 index 0000000..93eeda0 --- /dev/null +++ b/tests/ui-msrv/transmute-src-not-asbytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-msrv/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-msrv/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/include_value_not_from_bytes.rs b/tests/ui-nightly/include_value_not_from_bytes.rs new file mode 100644 index 0000000..45b6138 --- /dev/null +++ b/tests/ui-nightly/include_value_not_from_bytes.rs @@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[macro_use] +extern crate zerocopy; + +fn main() {} + +// Should fail because `UnsafeCell<u32>: !FromBytes`. +const NOT_FROM_BYTES: core::cell::UnsafeCell<u32> = + include_value!("../../testdata/include_value/data"); diff --git a/tests/ui-nightly/include_value_not_from_bytes.stderr b/tests/ui-nightly/include_value_not_from_bytes.stderr new file mode 100644 index 0000000..d948a0d --- /dev/null +++ b/tests/ui-nightly/include_value_not_from_bytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `UnsafeCell<u32>: FromBytes` is not satisfied + --> tests/ui-nightly/include_value_not_from_bytes.rs:12:5 + | +12 | include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `UnsafeCell<u32>` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-nightly/include_value_not_from_bytes.rs:12:5 + | +12 | include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-illegal.rs b/tests/ui-nightly/include_value_wrong_size.rs index 74b8439..d87b306 100644 --- a/tests/ui-msrv/transmute-illegal.rs +++ b/tests/ui-nightly/include_value_wrong_size.rs @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#[macro_use] extern crate zerocopy; fn main() {} -// It is unsound to inspect the usize value of a pointer during const eval. -const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); +// Should fail because the file is 4 bytes long, not 8. +const WRONG_SIZE: u64 = include_value!("../../testdata/include_value/data"); diff --git a/tests/ui-nightly/include_value_wrong_size.stderr b/tests/ui-nightly/include_value_wrong_size.stderr new file mode 100644 index 0000000..f592ece --- /dev/null +++ b/tests/ui-nightly/include_value_wrong_size.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/include_value_wrong_size.rs:11:25 + | +11 | const WRONG_SIZE: u64 = include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 4]` (32 bits) + = note: target type: `u64` (64 bits) + = note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/invalid-impls/invalid-impls.rs b/tests/ui-nightly/invalid-impls/invalid-impls.rs index b9a60bd..ea96390 100644 --- a/tests/ui-nightly/invalid-impls/invalid-impls.rs +++ b/tests/ui-nightly/invalid-impls/invalid-impls.rs @@ -1,6 +1,10 @@ -// Copyright 2022 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. // Since some macros from `macros.rs` are unused. #![allow(unused)] diff --git a/tests/ui-nightly/invalid-impls/invalid-impls.stderr b/tests/ui-nightly/invalid-impls/invalid-impls.stderr index 7d839ac..e5651d1 100644 --- a/tests/ui-nightly/invalid-impls/invalid-impls.stderr +++ b/tests/ui-nightly/invalid-impls/invalid-impls.stderr @@ -1,13 +1,13 @@ error[E0277]: the trait bound `T: zerocopy::FromZeroes` is not satisfied - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:22:37 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:26:37 | -22 | impl_or_verify!(T => FromZeroes for Foo<T>); +26 | impl_or_verify!(T => FromZeroes for Foo<T>); | ^^^^^^ the trait `zerocopy::FromZeroes` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::FromZeroes` - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:10 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:22:10 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-nightly/invalid-impls/../../../src/macros.rs @@ -15,26 +15,26 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:22:1 + ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:26:1 | -22 | impl_or_verify!(T => FromZeroes for Foo<T>); +26 | impl_or_verify!(T => FromZeroes for Foo<T>); | ------------------------------------------- in this macro invocation = note: this error originates in the derive macro `FromZeroes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -22 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>); - | ++++++++++++++++++++++ + | +26 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>); + | ++++++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:23:36 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:27:36 | -23 | impl_or_verify!(T => FromBytes for Foo<T>); +27 | impl_or_verify!(T => FromBytes for Foo<T>); | ^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::FromBytes` - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:22 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:22:22 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-nightly/invalid-impls/../../../src/macros.rs @@ -42,26 +42,26 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:23:1 + ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:27:1 | -23 | impl_or_verify!(T => FromBytes for Foo<T>); +27 | impl_or_verify!(T => FromBytes for Foo<T>); | ------------------------------------------ in this macro invocation = note: this error originates in the derive macro `FromBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -23 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>); - | +++++++++++++++++++++ + | +27 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>); + | +++++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:24:34 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:28:34 | -24 | impl_or_verify!(T => AsBytes for Foo<T>); +28 | impl_or_verify!(T => AsBytes for Foo<T>); | ^^^^^^ the trait `zerocopy::AsBytes` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::AsBytes` - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:33 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:22:33 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-nightly/invalid-impls/../../../src/macros.rs @@ -69,26 +69,26 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:24:1 + ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:28:1 | -24 | impl_or_verify!(T => AsBytes for Foo<T>); +28 | impl_or_verify!(T => AsBytes for Foo<T>); | ---------------------------------------- in this macro invocation = note: this error originates in the derive macro `AsBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -24 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>); - | +++++++++++++++++++ + | +28 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>); + | +++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:25:36 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:29:36 | -25 | impl_or_verify!(T => Unaligned for Foo<T>); +29 | impl_or_verify!(T => Unaligned for Foo<T>); | ^^^^^^ the trait `zerocopy::Unaligned` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::Unaligned` - --> tests/ui-nightly/invalid-impls/invalid-impls.rs:18:42 + --> tests/ui-nightly/invalid-impls/invalid-impls.rs:22:42 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-nightly/invalid-impls/../../../src/macros.rs @@ -96,12 +96,12 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:25:1 + ::: tests/ui-nightly/invalid-impls/invalid-impls.rs:29:1 | -25 | impl_or_verify!(T => Unaligned for Foo<T>); +29 | impl_or_verify!(T => Unaligned for Foo<T>); | ------------------------------------------ in this macro invocation = note: this error originates in the derive macro `Unaligned` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -25 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>); - | +++++++++++++++++++++ + | +29 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>); + | +++++++++++++++++++++ diff --git a/tests/ui-nightly/max-align.rs b/tests/ui-nightly/max-align.rs new file mode 100644 index 0000000..53e3eb9 --- /dev/null +++ b/tests/ui-nightly/max-align.rs @@ -0,0 +1,99 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +#[repr(C, align(1))] +struct Align1; + +#[repr(C, align(2))] +struct Align2; + +#[repr(C, align(4))] +struct Align4; + +#[repr(C, align(8))] +struct Align8; + +#[repr(C, align(16))] +struct Align16; + +#[repr(C, align(32))] +struct Align32; + +#[repr(C, align(64))] +struct Align64; + +#[repr(C, align(128))] +struct Align128; + +#[repr(C, align(256))] +struct Align256; + +#[repr(C, align(512))] +struct Align512; + +#[repr(C, align(1024))] +struct Align1024; + +#[repr(C, align(2048))] +struct Align2048; + +#[repr(C, align(4096))] +struct Align4096; + +#[repr(C, align(8192))] +struct Align8192; + +#[repr(C, align(16384))] +struct Align16384; + +#[repr(C, align(32768))] +struct Align32768; + +#[repr(C, align(65536))] +struct Align65536; + +#[repr(C, align(131072))] +struct Align131072; + +#[repr(C, align(262144))] +struct Align262144; + +#[repr(C, align(524288))] +struct Align524288; + +#[repr(C, align(1048576))] +struct Align1048576; + +#[repr(C, align(2097152))] +struct Align2097152; + +#[repr(C, align(4194304))] +struct Align4194304; + +#[repr(C, align(8388608))] +struct Align8388608; + +#[repr(C, align(16777216))] +struct Align16777216; + +#[repr(C, align(33554432))] +struct Align33554432; + +#[repr(C, align(67108864))] +struct Align67108864; + +#[repr(C, align(134217728))] +struct Align13421772; + +#[repr(C, align(268435456))] +struct Align26843545; + +#[repr(C, align(1073741824))] +struct Align1073741824; + +fn main() {} diff --git a/tests/ui-nightly/max-align.stderr b/tests/ui-nightly/max-align.stderr new file mode 100644 index 0000000..0cadb9a --- /dev/null +++ b/tests/ui-nightly/max-align.stderr @@ -0,0 +1,5 @@ +error[E0589]: invalid `repr(align)` attribute: larger than 2^29 + --> tests/ui-nightly/max-align.rs:96:11 + | +96 | #[repr(C, align(1073741824))] + | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-nightly/transmute-dst-not-frombytes.rs b/tests/ui-nightly/transmute-dst-not-frombytes.rs new file mode 100644 index 0000000..c4caaff --- /dev/null +++ b/tests/ui-nightly/transmute-dst-not-frombytes.rs @@ -0,0 +1,18 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); diff --git a/tests/ui-nightly/transmute-dst-not-frombytes.stderr b/tests/ui-nightly/transmute-dst-not-frombytes.stderr new file mode 100644 index 0000000..a9f1f7b --- /dev/null +++ b/tests/ui-nightly/transmute-dst-not-frombytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy: FromBytes` is not satisfied + --> tests/ui-nightly/transmute-dst-not-frombytes.rs:18:41 + | +18 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `NotZerocopy` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-nightly/transmute-dst-not-frombytes.rs:18:41 + | +18 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-illegal.stderr b/tests/ui-nightly/transmute-illegal.stderr deleted file mode 100644 index a57544b..0000000 --- a/tests/ui-nightly/transmute-illegal.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied - --> tests/ui-nightly/transmute-illegal.rs:10:30 - | -10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | the trait `AsBytes` is not implemented for `*const usize` - | required by a bound introduced by this call - | - = help: the trait `AsBytes` is implemented for `usize` -note: required by a bound in `POINTER_VALUE::transmute` - --> tests/ui-nightly/transmute-illegal.rs:10:30 - | -10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - = note: this error originates in the macro `zerocopy::transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-alignment-increase.rs b/tests/ui-nightly/transmute-mut-alignment-increase.rs new file mode 100644 index 0000000..0928564 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-alignment-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a type of smaller +// alignment to one of larger alignment. +const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); diff --git a/tests/ui-nightly/transmute-mut-alignment-increase.stderr b/tests/ui-nightly/transmute-mut-alignment-increase.stderr new file mode 100644 index 0000000..0666f8b --- /dev/null +++ b/tests/ui-nightly/transmute-mut-alignment-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-mut-alignment-increase.rs:19:39 + | +19 | const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<[u8; 2]>` (8 bits) + = note: target type: `MaxAlignsOf<[u8; 2], AU16>` (16 bits) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-const.rs b/tests/ui-nightly/transmute-mut-const.rs new file mode 100644 index 0000000..021b562 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-const.rs @@ -0,0 +1,20 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +const ARRAY_OF_U8S: [u8; 2] = [0u8; 2]; + +// `transmute_mut!` cannot, generally speaking, be used in const contexts. +const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); diff --git a/tests/ui-nightly/transmute-mut-const.stderr b/tests/ui-nightly/transmute-mut-const.stderr new file mode 100644 index 0000000..fa53ed0 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-const.stderr @@ -0,0 +1,42 @@ +warning: taking a mutable reference to a `const` item + --> tests/ui-nightly/transmute-mut-const.rs:20:52 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^ + | + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> tests/ui-nightly/transmute-mut-const.rs:17:1 + | +17 | const ARRAY_OF_U8S: [u8; 2] = [0u8; 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(const_item_mutation)]` on by default + +error[E0658]: mutable references are not allowed in constants + --> tests/ui-nightly/transmute-mut-const.rs:20:52 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + +error[E0015]: cannot call non-const fn `transmute_mut::<'_, '_, [u8; 2], [u8; 2]>` in constants + --> tests/ui-nightly/transmute-mut-const.rs:20:37 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> tests/ui-nightly/transmute-mut-const.rs:20:57 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | --------------------^^^^^^^^^^^^- + | | | + | | creates a temporary value which is freed while still in use + | temporary value is freed at the end of this statement + | using this value as a constant requires that borrow lasts for `'static` diff --git a/tests/ui-nightly/transmute-mut-dst-generic.rs b/tests/ui-nightly/transmute-mut-dst-generic.rs new file mode 100644 index 0000000..7068f10 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes>(u: &mut u8) -> &mut T { + // `transmute_mut!` requires the destination type to be concrete. + transmute_mut!(u) +} diff --git a/tests/ui-nightly/transmute-mut-dst-generic.stderr b/tests/ui-nightly/transmute-mut-dst-generic.stderr new file mode 100644 index 0000000..f278558 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-mut-dst-generic.rs:17:5 + | +17 | transmute_mut!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `T` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-mut-dst-generic.rs:17:5 + | +17 | transmute_mut!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<u8>` (8 bits) + = note: target type: `MaxAlignsOf<u8, T>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-dst-not-a-reference.rs b/tests/ui-nightly/transmute-mut-dst-not-a-reference.rs new file mode 100644 index 0000000..33a9ecd --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting into a non-reference +// destination type. +const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); diff --git a/tests/ui-nightly/transmute-mut-dst-not-a-reference.stderr b/tests/ui-nightly/transmute-mut-dst-not-a-reference.stderr new file mode 100644 index 0000000..a84547b --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-not-a-reference.stderr @@ -0,0 +1,39 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-dst-not-asbytes.rs b/tests/ui-nightly/transmute-mut-dst-not-asbytes.rs new file mode 100644 index 0000000..b72f129 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-not-asbytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the destination type implements `AsBytes` +const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-nightly/transmute-mut-dst-not-asbytes.stderr b/tests/ui-nightly/transmute-mut-dst-not-asbytes.stderr new file mode 100644 index 0000000..54c8e60 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-not-asbytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `Dst: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-mut-dst-not-asbytes.rs:24:36 + | +24 | const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `Dst` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-nightly/transmute-mut-dst-not-asbytes.rs:24:36 + | +24 | const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-dst-not-frombytes.rs b/tests/ui-nightly/transmute-mut-dst-not-frombytes.rs new file mode 100644 index 0000000..102fced --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-not-frombytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr b/tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr new file mode 100644 index 0000000..ea2123b --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `Dst: FromBytes` is not satisfied + --> tests/ui-nightly/transmute-mut-dst-not-frombytes.rs:24:38 + | +24 | const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `Dst` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-nightly/transmute-mut-dst-not-frombytes.rs:24:38 + | +24 | const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-dst-unsized.rs b/tests/ui-nightly/transmute-mut-dst-unsized.rs new file mode 100644 index 0000000..693ccda --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting into an unsized destination +// type. +const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); diff --git a/tests/ui-nightly/transmute-mut-dst-unsized.stderr b/tests/ui-nightly/transmute-mut-dst-unsized.stderr new file mode 100644 index 0000000..a670e25 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-dst-unsized.stderr @@ -0,0 +1,86 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-illegal-lifetime.rs b/tests/ui-nightly/transmute-mut-illegal-lifetime.rs new file mode 100644 index 0000000..c31765e --- /dev/null +++ b/tests/ui-nightly/transmute-mut-illegal-lifetime.rs @@ -0,0 +1,15 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +fn main() {} + +fn increase_lifetime() { + let mut x = 0u64; + // It is illegal to increase the lifetime scope. + let _: &'static mut u64 = zerocopy::transmute_mut!(&mut x); +} diff --git a/tests/ui-nightly/transmute-mut-illegal-lifetime.stderr b/tests/ui-nightly/transmute-mut-illegal-lifetime.stderr new file mode 100644 index 0000000..b826fcc --- /dev/null +++ b/tests/ui-nightly/transmute-mut-illegal-lifetime.stderr @@ -0,0 +1,12 @@ +error[E0597]: `x` does not live long enough + --> tests/ui-nightly/transmute-mut-illegal-lifetime.rs:14:56 + | +12 | let mut x = 0u64; + | ----- binding `x` declared here +13 | // It is illegal to increase the lifetime scope. +14 | let _: &'static mut u64 = zerocopy::transmute_mut!(&mut x); + | ---------------- ^^^^^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +15 | } + | - `x` dropped here while still borrowed diff --git a/tests/ui-nightly/transmute-mut-size-decrease.rs b/tests/ui-nightly/transmute-mut-size-decrease.rs new file mode 100644 index 0000000..c6eec3a --- /dev/null +++ b/tests/ui-nightly/transmute-mut-size-decrease.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// We require that the size of the destination type is not smaller than the size +// of the source type. +const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); diff --git a/tests/ui-nightly/transmute-mut-size-decrease.stderr b/tests/ui-nightly/transmute-mut-size-decrease.stderr new file mode 100644 index 0000000..ac1e35c --- /dev/null +++ b/tests/ui-nightly/transmute-mut-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-mut-size-decrease.rs:17:32 + | +17 | const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 2]` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-size-increase.rs b/tests/ui-nightly/transmute-mut-size-increase.rs new file mode 100644 index 0000000..a4657c2 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-size-increase.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); diff --git a/tests/ui-nightly/transmute-mut-size-increase.stderr b/tests/ui-nightly/transmute-mut-size-increase.stderr new file mode 100644 index 0000000..d343bd6 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-mut-size-increase.rs:17:37 + | +17 | const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `[u8; 2]` (16 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-src-dst-generic.rs b/tests/ui-nightly/transmute-mut-src-dst-generic.rs new file mode 100644 index 0000000..aed7ded --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-dst-generic.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes, U: AsBytes + FromBytes>(t: &mut T) -> &mut U { + // `transmute_mut!` requires the source and destination types to be + // concrete. + transmute_mut!(t) +} diff --git a/tests/ui-nightly/transmute-mut-src-dst-generic.stderr b/tests/ui-nightly/transmute-mut-src-dst-generic.stderr new file mode 100644 index 0000000..e3f3a3f --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-mut-src-dst-generic.rs:18:5 + | +18 | transmute_mut!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `U` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-mut-src-dst-generic.rs:18:5 + | +18 | transmute_mut!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, U>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-src-dst-not-references.rs b/tests/ui-nightly/transmute-mut-src-dst-not-references.rs new file mode 100644 index 0000000..98cc520 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-dst-not-references.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting between non-reference source +// and destination types. +const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(0usize); diff --git a/tests/ui-nightly/transmute-mut-src-dst-not-references.stderr b/tests/ui-nightly/transmute-mut-src-dst-not-references.stderr new file mode 100644 index 0000000..df3cf2d --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-dst-not-references.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-mut-src-dst-not-references.rs:17:59 + | +17 | const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&mut _`, found `usize` + | expected due to this + | + = note: expected mutable reference `&mut _` + found type `usize` +help: consider mutably borrowing here + | +17 | const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(&mut 0usize); + | ++++ diff --git a/tests/ui-nightly/transmute-mut-src-dst-unsized.rs b/tests/ui-nightly/transmute-mut-src-dst-unsized.rs new file mode 100644 index 0000000..1bebcf2 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting between unsized source and +// destination types. +const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); diff --git a/tests/ui-nightly/transmute-mut-src-dst-unsized.stderr b/tests/ui-nightly/transmute-mut-src-dst-unsized.stderr new file mode 100644 index 0000000..0f41a42 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-dst-unsized.stderr @@ -0,0 +1,231 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-src-generic.rs b/tests/ui-nightly/transmute-mut-src-generic.rs new file mode 100644 index 0000000..a3ef397 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes>(t: &mut T) -> &mut u8 { + // `transmute_mut!` requires the source type to be concrete. + transmute_mut!(t) +} diff --git a/tests/ui-nightly/transmute-mut-src-generic.stderr b/tests/ui-nightly/transmute-mut-src-generic.stderr new file mode 100644 index 0000000..c06d775 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-generic.stderr @@ -0,0 +1,10 @@ +error[E0405]: cannot find trait `FromBytes` in this scope + --> tests/ui-nightly/transmute-mut-src-generic.rs:15:31 + | +15 | fn transmute_mut<T: AsBytes + FromBytes>(t: &mut T) -> &mut u8 { + | ^^^^^^^^^ not found in this scope + | +help: consider importing this trait + | +11 + use zerocopy::FromBytes; + | diff --git a/tests/ui-nightly/transmute-mut-src-immutable.rs b/tests/ui-nightly/transmute-mut-src-immutable.rs new file mode 100644 index 0000000..08088d0 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-immutable.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +fn ref_src_immutable() { + // `transmute_mut!` requires that its source type be a mutable reference. + let _: &mut u8 = transmute_mut!(&0u8); +} diff --git a/tests/ui-nightly/transmute-mut-src-immutable.stderr b/tests/ui-nightly/transmute-mut-src-immutable.stderr new file mode 100644 index 0000000..7b7969d --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-immutable.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-mut-src-immutable.rs:17:37 + | +17 | let _: &mut u8 = transmute_mut!(&0u8); + | ---------------^^^^- + | | | + | | types differ in mutability + | expected due to this + | + = note: expected mutable reference `&mut _` + found reference `&u8` diff --git a/tests/ui-nightly/transmute-mut-src-not-a-reference.rs b/tests/ui-nightly/transmute-mut-src-not-a-reference.rs new file mode 100644 index 0000000..bf8bc32 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a non-reference source +// type. +const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(0usize); diff --git a/tests/ui-nightly/transmute-mut-src-not-a-reference.stderr b/tests/ui-nightly/transmute-mut-src-not-a-reference.stderr new file mode 100644 index 0000000..12b7674 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-not-a-reference.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-mut-src-not-a-reference.rs:17:53 + | +17 | const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&mut _`, found `usize` + | expected due to this + | + = note: expected mutable reference `&mut _` + found type `usize` +help: consider mutably borrowing here + | +17 | const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(&mut 0usize); + | ++++ diff --git a/tests/ui-nightly/transmute-mut-src-not-asbytes.rs b/tests/ui-nightly/transmute-mut-src-not-asbytes.rs new file mode 100644 index 0000000..6a14f12 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-not-asbytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-nightly/transmute-mut-src-not-asbytes.stderr b/tests/ui-nightly/transmute-mut-src-not-asbytes.stderr new file mode 100644 index 0000000..b755d3c --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-not-asbytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `Src: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `Src` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-nightly/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Src: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `Src` + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-nightly/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-src-not-frombytes.rs b/tests/ui-nightly/transmute-mut-src-not-frombytes.rs new file mode 100644 index 0000000..2ebe036 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-not-frombytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the source type implements `FromBytes` +const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-nightly/transmute-mut-src-not-frombytes.stderr b/tests/ui-nightly/transmute-mut-src-not-frombytes.stderr new file mode 100644 index 0000000..5a9f0a7 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-not-frombytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `Src: FromBytes` is not satisfied + --> tests/ui-nightly/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `Src` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-nightly/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Src: FromBytes` is not satisfied + --> tests/ui-nightly/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `Src` + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-nightly/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-mut-src-unsized.rs b/tests/ui-nightly/transmute-mut-src-unsized.rs new file mode 100644 index 0000000..413dd68 --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-unsized.rs @@ -0,0 +1,16 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from an unsized source type. +const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); diff --git a/tests/ui-nightly/transmute-mut-src-unsized.stderr b/tests/ui-nightly/transmute-mut-src-unsized.stderr new file mode 100644 index 0000000..99475ad --- /dev/null +++ b/tests/ui-nightly/transmute-mut-src-unsized.stderr @@ -0,0 +1,158 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ptr-to-usize.rs b/tests/ui-nightly/transmute-ptr-to-usize.rs new file mode 100644 index 0000000..5af8859 --- /dev/null +++ b/tests/ui-nightly/transmute-ptr-to-usize.rs @@ -0,0 +1,20 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// It is unclear whether we can or should support this transmutation, especially +// in a const context. This test ensures that even if such a transmutation +// becomes valid due to the requisite implementations of `FromBytes` being +// added, that we re-examine whether it should specifically be valid in a const +// context. +const POINTER_VALUE: usize = transmute!(&0usize as *const usize); diff --git a/tests/ui-nightly/transmute-ptr-to-usize.stderr b/tests/ui-nightly/transmute-ptr-to-usize.stderr new file mode 100644 index 0000000..2fcba2f --- /dev/null +++ b/tests/ui-nightly/transmute-ptr-to-usize.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `*const usize` + | required by a bound introduced by this call + | + = help: the trait `AsBytes` is implemented for `usize` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `*const usize` + | + = help: the trait `AsBytes` is implemented for `usize` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-alignment-increase.rs b/tests/ui-nightly/transmute-ref-alignment-increase.rs new file mode 100644 index 0000000..bf1988c --- /dev/null +++ b/tests/ui-nightly/transmute-ref-alignment-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a type of smaller +// alignment to one of larger alignment. +const INCREASE_ALIGNMENT: &AU16 = transmute_ref!(&[0u8; 2]); diff --git a/tests/ui-nightly/transmute-ref-alignment-increase.stderr b/tests/ui-nightly/transmute-ref-alignment-increase.stderr new file mode 100644 index 0000000..1cef246 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-alignment-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-alignment-increase.rs:19:35 + | +19 | const INCREASE_ALIGNMENT: &AU16 = transmute_ref!(&[0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<[u8; 2]>` (8 bits) + = note: target type: `MaxAlignsOf<[u8; 2], AU16>` (16 bits) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-dst-generic.rs b/tests/ui-nightly/transmute-ref-dst-generic.rs new file mode 100644 index 0000000..bf4a0f9 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, FromBytes}; + +fn main() {} + +fn transmute_ref<T: FromBytes>(u: &u8) -> &T { + // `transmute_ref!` requires the destination type to be concrete. + transmute_ref!(u) +} diff --git a/tests/ui-nightly/transmute-ref-dst-generic.stderr b/tests/ui-nightly/transmute-ref-dst-generic.stderr new file mode 100644 index 0000000..4c94d50 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-dst-generic.rs:17:5 + | +17 | transmute_ref!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `T` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-dst-generic.rs:17:5 + | +17 | transmute_ref!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<u8>` (8 bits) + = note: target type: `MaxAlignsOf<u8, T>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-dst-mutable.rs b/tests/ui-nightly/transmute-ref-dst-mutable.rs new file mode 100644 index 0000000..fa0e6e4 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-mutable.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +fn ref_dst_mutable() { + // `transmute_ref!` requires that its destination type be an immutable + // reference. + let _: &mut u8 = transmute_ref!(&0u8); +} diff --git a/tests/ui-nightly/transmute-ref-dst-mutable.stderr b/tests/ui-nightly/transmute-ref-dst-mutable.stderr new file mode 100644 index 0000000..0cbdd17 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-mutable.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-dst-not-a-reference.rs b/tests/ui-nightly/transmute-ref-dst-not-a-reference.rs new file mode 100644 index 0000000..de55f9a --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting into a non-reference +// destination type. +const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); diff --git a/tests/ui-nightly/transmute-ref-dst-not-a-reference.stderr b/tests/ui-nightly/transmute-ref-dst-not-a-reference.stderr new file mode 100644 index 0000000..847d547 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-not-a-reference.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-dst-not-frombytes.rs b/tests/ui-nightly/transmute-ref-dst-not-frombytes.rs new file mode 100644 index 0000000..d81f64d --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-not-frombytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); diff --git a/tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr b/tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr new file mode 100644 index 0000000..e4791d7 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy: FromBytes` is not satisfied + --> tests/ui-nightly/transmute-ref-dst-not-frombytes.rs:18:42 + | +18 | const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `NotZerocopy` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-nightly/transmute-ref-dst-not-frombytes.rs:18:42 + | +18 | const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-dst-unsized.rs b/tests/ui-nightly/transmute-ref-dst-unsized.rs new file mode 100644 index 0000000..625f1fa --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting into an unsized destination +// type. +const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); diff --git a/tests/ui-nightly/transmute-ref-dst-unsized.stderr b/tests/ui-nightly/transmute-ref-dst-unsized.stderr new file mode 100644 index 0000000..3d0f6d0 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-dst-unsized.stderr @@ -0,0 +1,69 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-nightly/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-illegal-lifetime.rs b/tests/ui-nightly/transmute-ref-illegal-lifetime.rs new file mode 100644 index 0000000..8dd191e --- /dev/null +++ b/tests/ui-nightly/transmute-ref-illegal-lifetime.rs @@ -0,0 +1,15 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +fn main() {} + +fn increase_lifetime() { + let x = 0u64; + // It is illegal to increase the lifetime scope. + let _: &'static u64 = zerocopy::transmute_ref!(&x); +} diff --git a/tests/ui-nightly/transmute-ref-illegal-lifetime.stderr b/tests/ui-nightly/transmute-ref-illegal-lifetime.stderr new file mode 100644 index 0000000..e16a557 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-illegal-lifetime.stderr @@ -0,0 +1,12 @@ +error[E0597]: `x` does not live long enough + --> tests/ui-nightly/transmute-ref-illegal-lifetime.rs:14:52 + | +12 | let x = 0u64; + | - binding `x` declared here +13 | // It is illegal to increase the lifetime scope. +14 | let _: &'static u64 = zerocopy::transmute_ref!(&x); + | ------------ ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +15 | } + | - `x` dropped here while still borrowed diff --git a/tests/ui-nightly/transmute-ref-size-decrease.rs b/tests/ui-nightly/transmute-ref-size-decrease.rs new file mode 100644 index 0000000..1d66a54 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-size-decrease.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// Although this is not a soundness requirement, we currently require that the +// size of the destination type is not smaller than the size of the source type. +const DECREASE_SIZE: &u8 = transmute_ref!(&[0u8; 2]); diff --git a/tests/ui-nightly/transmute-ref-size-decrease.stderr b/tests/ui-nightly/transmute-ref-size-decrease.stderr new file mode 100644 index 0000000..793ecc5 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-size-decrease.rs:17:28 + | +17 | const DECREASE_SIZE: &u8 = transmute_ref!(&[0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 2]` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-size-increase.rs b/tests/ui-nightly/transmute-ref-size-increase.rs new file mode 100644 index 0000000..cdca560 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-size-increase.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: &[u8; 2] = transmute_ref!(&0u8); diff --git a/tests/ui-nightly/transmute-ref-size-increase.stderr b/tests/ui-nightly/transmute-ref-size-increase.stderr new file mode 100644 index 0000000..40c69f6 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-size-increase.rs:17:33 + | +17 | const INCREASE_SIZE: &[u8; 2] = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `[u8; 2]` (16 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-src-dst-generic.rs b/tests/ui-nightly/transmute-ref-src-dst-generic.rs new file mode 100644 index 0000000..409d785 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-dst-generic.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_ref<T: AsBytes, U: FromBytes>(t: &T) -> &U { + // `transmute_ref!` requires the source and destination types to be + // concrete. + transmute_ref!(t) +} diff --git a/tests/ui-nightly/transmute-ref-src-dst-generic.stderr b/tests/ui-nightly/transmute-ref-src-dst-generic.stderr new file mode 100644 index 0000000..6a3a4fd --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-src-dst-generic.rs:18:5 + | +18 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `U` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-src-dst-generic.rs:18:5 + | +18 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, U>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-src-dst-not-references.rs b/tests/ui-nightly/transmute-ref-src-dst-not-references.rs new file mode 100644 index 0000000..114e917 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-dst-not-references.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting between non-reference source +// and destination types. +const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); diff --git a/tests/ui-nightly/transmute-ref-src-dst-not-references.stderr b/tests/ui-nightly/transmute-ref-src-dst-not-references.stderr new file mode 100644 index 0000000..0f1f7fc --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-dst-not-references.stderr @@ -0,0 +1,45 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-src-dst-not-references.rs:17:54 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&_`, found `usize` + | expected due to this + | + = note: expected reference `&_` + found type `usize` +help: consider borrowing here + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(&0usize); + | + + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-src-dst-unsized.rs b/tests/ui-nightly/transmute-ref-src-dst-unsized.rs new file mode 100644 index 0000000..6bfe7ff --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting between unsized source and +// destination types. +const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); diff --git a/tests/ui-nightly/transmute-ref-src-dst-unsized.stderr b/tests/ui-nightly/transmute-ref-src-dst-unsized.stderr new file mode 100644 index 0000000..02e62bc --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-dst-unsized.stderr @@ -0,0 +1,183 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-src-generic.rs b/tests/ui-nightly/transmute-ref-src-generic.rs new file mode 100644 index 0000000..010281c --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, AsBytes}; + +fn main() {} + +fn transmute_ref<T: AsBytes>(t: &T) -> &u8 { + // `transmute_ref!` requires the source type to be concrete. + transmute_ref!(t) +} diff --git a/tests/ui-nightly/transmute-ref-src-generic.stderr b/tests/ui-nightly/transmute-ref-src-generic.stderr new file mode 100644 index 0000000..a168f44 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-src-generic.rs:17:5 + | +17 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-ref-src-generic.rs:17:5 + | +17 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, u8>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-src-not-a-reference.rs b/tests/ui-nightly/transmute-ref-src-not-a-reference.rs new file mode 100644 index 0000000..90661b3 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a non-reference source +// type. +const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(0usize); diff --git a/tests/ui-nightly/transmute-ref-src-not-a-reference.stderr b/tests/ui-nightly/transmute-ref-src-not-a-reference.stderr new file mode 100644 index 0000000..be477c6 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-not-a-reference.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> tests/ui-nightly/transmute-ref-src-not-a-reference.rs:17:49 + | +17 | const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&_`, found `usize` + | expected due to this + | + = note: expected reference `&_` + found type `usize` +help: consider borrowing here + | +17 | const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(&0usize); + | + diff --git a/tests/ui-nightly/transmute-ref-src-not-asbytes.rs b/tests/ui-nightly/transmute-ref-src-not-asbytes.rs new file mode 100644 index 0000000..6ab19f3 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-not-asbytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); diff --git a/tests/ui-nightly/transmute-ref-src-not-asbytes.stderr b/tests/ui-nightly/transmute-ref-src-not-asbytes.stderr new file mode 100644 index 0000000..eb28ccf --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-not-asbytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ref-src-unsized.rs b/tests/ui-nightly/transmute-ref-src-unsized.rs new file mode 100644 index 0000000..14e72b4 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-unsized.rs @@ -0,0 +1,16 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from an unsized source type. +const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); diff --git a/tests/ui-nightly/transmute-ref-src-unsized.stderr b/tests/ui-nightly/transmute-ref-src-unsized.stderr new file mode 100644 index 0000000..b280429 --- /dev/null +++ b/tests/ui-nightly/transmute-ref-src-unsized.stderr @@ -0,0 +1,127 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-nightly/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-size-decrease.rs b/tests/ui-nightly/transmute-size-decrease.rs new file mode 100644 index 0000000..1d56831 --- /dev/null +++ b/tests/ui-nightly/transmute-size-decrease.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// Although this is not a soundness requirement, we currently require that the +// size of the destination type is not smaller than the size of the source type. +const DECREASE_SIZE: u8 = transmute!(AU16(0)); diff --git a/tests/ui-nightly/transmute-size-decrease.stderr b/tests/ui-nightly/transmute-size-decrease.stderr new file mode 100644 index 0000000..83742d7 --- /dev/null +++ b/tests/ui-nightly/transmute-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-size-decrease.rs:19:27 + | +19 | const DECREASE_SIZE: u8 = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AU16` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-size-increase.rs b/tests/ui-nightly/transmute-size-increase.rs new file mode 100644 index 0000000..32f9363 --- /dev/null +++ b/tests/ui-nightly/transmute-size-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: AU16 = transmute!(0u8); diff --git a/tests/ui-nightly/transmute-size-increase.stderr b/tests/ui-nightly/transmute-size-increase.stderr new file mode 100644 index 0000000..230bb17 --- /dev/null +++ b/tests/ui-nightly/transmute-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-size-increase.rs:19:29 + | +19 | const INCREASE_SIZE: AU16 = transmute!(0u8); + | ^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `AU16` (16 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-src-not-asbytes.rs b/tests/ui-nightly/transmute-src-not-asbytes.rs new file mode 100644 index 0000000..dd73021 --- /dev/null +++ b/tests/ui-nightly/transmute-src-not-asbytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); diff --git a/tests/ui-nightly/transmute-src-not-asbytes.stderr b/tests/ui-nightly/transmute-src-not-asbytes.stderr new file mode 100644 index 0000000..b36a820 --- /dev/null +++ b/tests/ui-nightly/transmute-src-not-asbytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-nightly/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-nightly/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/include_value_not_from_bytes.rs b/tests/ui-stable/include_value_not_from_bytes.rs new file mode 100644 index 0000000..45b6138 --- /dev/null +++ b/tests/ui-stable/include_value_not_from_bytes.rs @@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[macro_use] +extern crate zerocopy; + +fn main() {} + +// Should fail because `UnsafeCell<u32>: !FromBytes`. +const NOT_FROM_BYTES: core::cell::UnsafeCell<u32> = + include_value!("../../testdata/include_value/data"); diff --git a/tests/ui-stable/include_value_not_from_bytes.stderr b/tests/ui-stable/include_value_not_from_bytes.stderr new file mode 100644 index 0000000..7e9a035 --- /dev/null +++ b/tests/ui-stable/include_value_not_from_bytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `UnsafeCell<u32>: FromBytes` is not satisfied + --> tests/ui-stable/include_value_not_from_bytes.rs:12:5 + | +12 | include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `UnsafeCell<u32>` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-stable/include_value_not_from_bytes.rs:12:5 + | +12 | include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-illegal.rs b/tests/ui-stable/include_value_wrong_size.rs index 74b8439..d87b306 100644 --- a/tests/ui-nightly/transmute-illegal.rs +++ b/tests/ui-stable/include_value_wrong_size.rs @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#[macro_use] extern crate zerocopy; fn main() {} -// It is unsound to inspect the usize value of a pointer during const eval. -const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); +// Should fail because the file is 4 bytes long, not 8. +const WRONG_SIZE: u64 = include_value!("../../testdata/include_value/data"); diff --git a/tests/ui-stable/include_value_wrong_size.stderr b/tests/ui-stable/include_value_wrong_size.stderr new file mode 100644 index 0000000..956d74c --- /dev/null +++ b/tests/ui-stable/include_value_wrong_size.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/include_value_wrong_size.rs:11:25 + | +11 | const WRONG_SIZE: u64 = include_value!("../../testdata/include_value/data"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 4]` (32 bits) + = note: target type: `u64` (64 bits) + = note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/invalid-impls/invalid-impls.rs b/tests/ui-stable/invalid-impls/invalid-impls.rs index b9a60bd..ea96390 100644 --- a/tests/ui-stable/invalid-impls/invalid-impls.rs +++ b/tests/ui-stable/invalid-impls/invalid-impls.rs @@ -1,6 +1,10 @@ -// Copyright 2022 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. // Since some macros from `macros.rs` are unused. #![allow(unused)] diff --git a/tests/ui-stable/invalid-impls/invalid-impls.stderr b/tests/ui-stable/invalid-impls/invalid-impls.stderr index f613377..7737d67 100644 --- a/tests/ui-stable/invalid-impls/invalid-impls.stderr +++ b/tests/ui-stable/invalid-impls/invalid-impls.stderr @@ -1,13 +1,13 @@ error[E0277]: the trait bound `T: zerocopy::FromZeroes` is not satisfied - --> tests/ui-stable/invalid-impls/invalid-impls.rs:22:37 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:26:37 | -22 | impl_or_verify!(T => FromZeroes for Foo<T>); +26 | impl_or_verify!(T => FromZeroes for Foo<T>); | ^^^^^^ the trait `zerocopy::FromZeroes` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::FromZeroes` - --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:10 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:22:10 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-stable/invalid-impls/../../../src/macros.rs @@ -15,26 +15,26 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-stable/invalid-impls/invalid-impls.rs:22:1 + ::: tests/ui-stable/invalid-impls/invalid-impls.rs:26:1 | -22 | impl_or_verify!(T => FromZeroes for Foo<T>); +26 | impl_or_verify!(T => FromZeroes for Foo<T>); | ------------------------------------------- in this macro invocation = note: this error originates in the derive macro `FromZeroes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -22 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>); - | ++++++++++++++++++++++ + | +26 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>); + | ++++++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied - --> tests/ui-stable/invalid-impls/invalid-impls.rs:23:36 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:27:36 | -23 | impl_or_verify!(T => FromBytes for Foo<T>); +27 | impl_or_verify!(T => FromBytes for Foo<T>); | ^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::FromBytes` - --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:22 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:22:22 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-stable/invalid-impls/../../../src/macros.rs @@ -42,26 +42,26 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-stable/invalid-impls/invalid-impls.rs:23:1 + ::: tests/ui-stable/invalid-impls/invalid-impls.rs:27:1 | -23 | impl_or_verify!(T => FromBytes for Foo<T>); +27 | impl_or_verify!(T => FromBytes for Foo<T>); | ------------------------------------------ in this macro invocation = note: this error originates in the derive macro `FromBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -23 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>); - | +++++++++++++++++++++ + | +27 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>); + | +++++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied - --> tests/ui-stable/invalid-impls/invalid-impls.rs:24:34 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:28:34 | -24 | impl_or_verify!(T => AsBytes for Foo<T>); +28 | impl_or_verify!(T => AsBytes for Foo<T>); | ^^^^^^ the trait `zerocopy::AsBytes` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::AsBytes` - --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:33 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:22:33 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-stable/invalid-impls/../../../src/macros.rs @@ -69,26 +69,26 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-stable/invalid-impls/invalid-impls.rs:24:1 + ::: tests/ui-stable/invalid-impls/invalid-impls.rs:28:1 | -24 | impl_or_verify!(T => AsBytes for Foo<T>); +28 | impl_or_verify!(T => AsBytes for Foo<T>); | ---------------------------------------- in this macro invocation = note: this error originates in the derive macro `AsBytes` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -24 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>); - | +++++++++++++++++++ + | +28 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>); + | +++++++++++++++++++ error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied - --> tests/ui-stable/invalid-impls/invalid-impls.rs:25:36 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:29:36 | -25 | impl_or_verify!(T => Unaligned for Foo<T>); +29 | impl_or_verify!(T => Unaligned for Foo<T>); | ^^^^^^ the trait `zerocopy::Unaligned` is not implemented for `T` | note: required for `Foo<T>` to implement `zerocopy::Unaligned` - --> tests/ui-stable/invalid-impls/invalid-impls.rs:18:42 + --> tests/ui-stable/invalid-impls/invalid-impls.rs:22:42 | -18 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] +22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `_::Subtrait` --> tests/ui-stable/invalid-impls/../../../src/macros.rs @@ -96,12 +96,12 @@ note: required by a bound in `_::Subtrait` | trait Subtrait: $trait {} | ^^^^^^ required by this bound in `Subtrait` | - ::: tests/ui-stable/invalid-impls/invalid-impls.rs:25:1 + ::: tests/ui-stable/invalid-impls/invalid-impls.rs:29:1 | -25 | impl_or_verify!(T => Unaligned for Foo<T>); +29 | impl_or_verify!(T => Unaligned for Foo<T>); | ------------------------------------------ in this macro invocation = note: this error originates in the derive macro `Unaligned` which comes from the expansion of the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` - | -25 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>); - | +++++++++++++++++++++ + | +29 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>); + | +++++++++++++++++++++ diff --git a/tests/ui-stable/max-align.rs b/tests/ui-stable/max-align.rs new file mode 100644 index 0000000..53e3eb9 --- /dev/null +++ b/tests/ui-stable/max-align.rs @@ -0,0 +1,99 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +#[repr(C, align(1))] +struct Align1; + +#[repr(C, align(2))] +struct Align2; + +#[repr(C, align(4))] +struct Align4; + +#[repr(C, align(8))] +struct Align8; + +#[repr(C, align(16))] +struct Align16; + +#[repr(C, align(32))] +struct Align32; + +#[repr(C, align(64))] +struct Align64; + +#[repr(C, align(128))] +struct Align128; + +#[repr(C, align(256))] +struct Align256; + +#[repr(C, align(512))] +struct Align512; + +#[repr(C, align(1024))] +struct Align1024; + +#[repr(C, align(2048))] +struct Align2048; + +#[repr(C, align(4096))] +struct Align4096; + +#[repr(C, align(8192))] +struct Align8192; + +#[repr(C, align(16384))] +struct Align16384; + +#[repr(C, align(32768))] +struct Align32768; + +#[repr(C, align(65536))] +struct Align65536; + +#[repr(C, align(131072))] +struct Align131072; + +#[repr(C, align(262144))] +struct Align262144; + +#[repr(C, align(524288))] +struct Align524288; + +#[repr(C, align(1048576))] +struct Align1048576; + +#[repr(C, align(2097152))] +struct Align2097152; + +#[repr(C, align(4194304))] +struct Align4194304; + +#[repr(C, align(8388608))] +struct Align8388608; + +#[repr(C, align(16777216))] +struct Align16777216; + +#[repr(C, align(33554432))] +struct Align33554432; + +#[repr(C, align(67108864))] +struct Align67108864; + +#[repr(C, align(134217728))] +struct Align13421772; + +#[repr(C, align(268435456))] +struct Align26843545; + +#[repr(C, align(1073741824))] +struct Align1073741824; + +fn main() {} diff --git a/tests/ui-stable/max-align.stderr b/tests/ui-stable/max-align.stderr new file mode 100644 index 0000000..ea472f2 --- /dev/null +++ b/tests/ui-stable/max-align.stderr @@ -0,0 +1,5 @@ +error[E0589]: invalid `repr(align)` attribute: larger than 2^29 + --> tests/ui-stable/max-align.rs:96:11 + | +96 | #[repr(C, align(1073741824))] + | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-stable/transmute-dst-not-frombytes.rs b/tests/ui-stable/transmute-dst-not-frombytes.rs new file mode 100644 index 0000000..c4caaff --- /dev/null +++ b/tests/ui-stable/transmute-dst-not-frombytes.rs @@ -0,0 +1,18 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); diff --git a/tests/ui-stable/transmute-dst-not-frombytes.stderr b/tests/ui-stable/transmute-dst-not-frombytes.stderr new file mode 100644 index 0000000..b008bcd --- /dev/null +++ b/tests/ui-stable/transmute-dst-not-frombytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy: FromBytes` is not satisfied + --> tests/ui-stable/transmute-dst-not-frombytes.rs:18:41 + | +18 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `NotZerocopy` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-stable/transmute-dst-not-frombytes.rs:18:41 + | +18 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-illegal.stderr b/tests/ui-stable/transmute-illegal.stderr deleted file mode 100644 index e9ac240..0000000 --- a/tests/ui-stable/transmute-illegal.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied - --> tests/ui-stable/transmute-illegal.rs:10:30 - | -10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | the trait `AsBytes` is not implemented for `*const usize` - | required by a bound introduced by this call - | - = help: the trait `AsBytes` is implemented for `usize` -note: required by a bound in `POINTER_VALUE::transmute` - --> tests/ui-stable/transmute-illegal.rs:10:30 - | -10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - = note: this error originates in the macro `zerocopy::transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-alignment-increase.rs b/tests/ui-stable/transmute-mut-alignment-increase.rs new file mode 100644 index 0000000..0928564 --- /dev/null +++ b/tests/ui-stable/transmute-mut-alignment-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a type of smaller +// alignment to one of larger alignment. +const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); diff --git a/tests/ui-stable/transmute-mut-alignment-increase.stderr b/tests/ui-stable/transmute-mut-alignment-increase.stderr new file mode 100644 index 0000000..252fec9 --- /dev/null +++ b/tests/ui-stable/transmute-mut-alignment-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-mut-alignment-increase.rs:19:39 + | +19 | const INCREASE_ALIGNMENT: &mut AU16 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<[u8; 2]>` (8 bits) + = note: target type: `MaxAlignsOf<[u8; 2], AU16>` (16 bits) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-const.rs b/tests/ui-stable/transmute-mut-const.rs new file mode 100644 index 0000000..021b562 --- /dev/null +++ b/tests/ui-stable/transmute-mut-const.rs @@ -0,0 +1,20 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +const ARRAY_OF_U8S: [u8; 2] = [0u8; 2]; + +// `transmute_mut!` cannot, generally speaking, be used in const contexts. +const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); diff --git a/tests/ui-stable/transmute-mut-const.stderr b/tests/ui-stable/transmute-mut-const.stderr new file mode 100644 index 0000000..a89ea67 --- /dev/null +++ b/tests/ui-stable/transmute-mut-const.stderr @@ -0,0 +1,41 @@ +warning: taking a mutable reference to a `const` item + --> tests/ui-stable/transmute-mut-const.rs:20:52 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^ + | + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> tests/ui-stable/transmute-mut-const.rs:17:1 + | +17 | const ARRAY_OF_U8S: [u8; 2] = [0u8; 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(const_item_mutation)]` on by default + +error[E0658]: mutable references are not allowed in constants + --> tests/ui-stable/transmute-mut-const.rs:20:52 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information + +error[E0015]: cannot call non-const fn `transmute_mut::<'_, '_, [u8; 2], [u8; 2]>` in constants + --> tests/ui-stable/transmute-mut-const.rs:20:37 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> tests/ui-stable/transmute-mut-const.rs:20:57 + | +20 | const CONST_CONTEXT: &mut [u8; 2] = transmute_mut!(&mut ARRAY_OF_U8S); + | --------------------^^^^^^^^^^^^- + | | | + | | creates a temporary value which is freed while still in use + | temporary value is freed at the end of this statement + | using this value as a constant requires that borrow lasts for `'static` diff --git a/tests/ui-stable/transmute-mut-dst-generic.rs b/tests/ui-stable/transmute-mut-dst-generic.rs new file mode 100644 index 0000000..7068f10 --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes>(u: &mut u8) -> &mut T { + // `transmute_mut!` requires the destination type to be concrete. + transmute_mut!(u) +} diff --git a/tests/ui-stable/transmute-mut-dst-generic.stderr b/tests/ui-stable/transmute-mut-dst-generic.stderr new file mode 100644 index 0000000..0000eb0 --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-mut-dst-generic.rs:17:5 + | +17 | transmute_mut!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `T` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-mut-dst-generic.rs:17:5 + | +17 | transmute_mut!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<u8>` (8 bits) + = note: target type: `MaxAlignsOf<u8, T>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-dst-not-a-reference.rs b/tests/ui-stable/transmute-mut-dst-not-a-reference.rs new file mode 100644 index 0000000..33a9ecd --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting into a non-reference +// destination type. +const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); diff --git a/tests/ui-stable/transmute-mut-dst-not-a-reference.stderr b/tests/ui-stable/transmute-mut-dst-not-a-reference.stderr new file mode 100644 index 0000000..14ee444 --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-not-a-reference.stderr @@ -0,0 +1,39 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-mut-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&mut _` + | + = note: expected type `usize` + found mutable reference `&mut _` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-dst-not-asbytes.rs b/tests/ui-stable/transmute-mut-dst-not-asbytes.rs new file mode 100644 index 0000000..b72f129 --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-not-asbytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the destination type implements `AsBytes` +const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-stable/transmute-mut-dst-not-asbytes.stderr b/tests/ui-stable/transmute-mut-dst-not-asbytes.stderr new file mode 100644 index 0000000..f3cacca --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-not-asbytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `Dst: AsBytes` is not satisfied + --> tests/ui-stable/transmute-mut-dst-not-asbytes.rs:24:36 + | +24 | const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `Dst` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-stable/transmute-mut-dst-not-asbytes.rs:24:36 + | +24 | const DST_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-dst-not-frombytes.rs b/tests/ui-stable/transmute-mut-dst-not-frombytes.rs new file mode 100644 index 0000000..102fced --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-not-frombytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-stable/transmute-mut-dst-not-frombytes.stderr b/tests/ui-stable/transmute-mut-dst-not-frombytes.stderr new file mode 100644 index 0000000..39bb4fd --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-not-frombytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `Dst: FromBytes` is not satisfied + --> tests/ui-stable/transmute-mut-dst-not-frombytes.rs:24:38 + | +24 | const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `Dst` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-stable/transmute-mut-dst-not-frombytes.rs:24:38 + | +24 | const DST_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-dst-unsized.rs b/tests/ui-stable/transmute-mut-dst-unsized.rs new file mode 100644 index 0000000..693ccda --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting into an unsized destination +// type. +const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); diff --git a/tests/ui-stable/transmute-mut-dst-unsized.stderr b/tests/ui-stable/transmute-mut-dst-unsized.stderr new file mode 100644 index 0000000..0772785 --- /dev/null +++ b/tests/ui-stable/transmute-mut-dst-unsized.stderr @@ -0,0 +1,106 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-dst-unsized.rs:17:32 + | +17 | const DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-illegal-lifetime.rs b/tests/ui-stable/transmute-mut-illegal-lifetime.rs new file mode 100644 index 0000000..c31765e --- /dev/null +++ b/tests/ui-stable/transmute-mut-illegal-lifetime.rs @@ -0,0 +1,15 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +fn main() {} + +fn increase_lifetime() { + let mut x = 0u64; + // It is illegal to increase the lifetime scope. + let _: &'static mut u64 = zerocopy::transmute_mut!(&mut x); +} diff --git a/tests/ui-stable/transmute-mut-illegal-lifetime.stderr b/tests/ui-stable/transmute-mut-illegal-lifetime.stderr new file mode 100644 index 0000000..7f12813 --- /dev/null +++ b/tests/ui-stable/transmute-mut-illegal-lifetime.stderr @@ -0,0 +1,12 @@ +error[E0597]: `x` does not live long enough + --> tests/ui-stable/transmute-mut-illegal-lifetime.rs:14:56 + | +12 | let mut x = 0u64; + | ----- binding `x` declared here +13 | // It is illegal to increase the lifetime scope. +14 | let _: &'static mut u64 = zerocopy::transmute_mut!(&mut x); + | ---------------- ^^^^^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +15 | } + | - `x` dropped here while still borrowed diff --git a/tests/ui-stable/transmute-mut-size-decrease.rs b/tests/ui-stable/transmute-mut-size-decrease.rs new file mode 100644 index 0000000..c6eec3a --- /dev/null +++ b/tests/ui-stable/transmute-mut-size-decrease.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// We require that the size of the destination type is not smaller than the size +// of the source type. +const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); diff --git a/tests/ui-stable/transmute-mut-size-decrease.stderr b/tests/ui-stable/transmute-mut-size-decrease.stderr new file mode 100644 index 0000000..2399913 --- /dev/null +++ b/tests/ui-stable/transmute-mut-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-mut-size-decrease.rs:17:32 + | +17 | const DECREASE_SIZE: &mut u8 = transmute_mut!(&mut [0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 2]` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-size-increase.rs b/tests/ui-stable/transmute-mut-size-increase.rs new file mode 100644 index 0000000..a4657c2 --- /dev/null +++ b/tests/ui-stable/transmute-mut-size-increase.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); diff --git a/tests/ui-stable/transmute-mut-size-increase.stderr b/tests/ui-stable/transmute-mut-size-increase.stderr new file mode 100644 index 0000000..1427c7b --- /dev/null +++ b/tests/ui-stable/transmute-mut-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-mut-size-increase.rs:17:37 + | +17 | const INCREASE_SIZE: &mut [u8; 2] = transmute_mut!(&mut 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `[u8; 2]` (16 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-src-dst-generic.rs b/tests/ui-stable/transmute-mut-src-dst-generic.rs new file mode 100644 index 0000000..aed7ded --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-dst-generic.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes, U: AsBytes + FromBytes>(t: &mut T) -> &mut U { + // `transmute_mut!` requires the source and destination types to be + // concrete. + transmute_mut!(t) +} diff --git a/tests/ui-stable/transmute-mut-src-dst-generic.stderr b/tests/ui-stable/transmute-mut-src-dst-generic.stderr new file mode 100644 index 0000000..ddb8bb6 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-mut-src-dst-generic.rs:18:5 + | +18 | transmute_mut!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `U` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-mut-src-dst-generic.rs:18:5 + | +18 | transmute_mut!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, U>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-src-dst-not-references.rs b/tests/ui-stable/transmute-mut-src-dst-not-references.rs new file mode 100644 index 0000000..98cc520 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-dst-not-references.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting between non-reference source +// and destination types. +const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(0usize); diff --git a/tests/ui-stable/transmute-mut-src-dst-not-references.stderr b/tests/ui-stable/transmute-mut-src-dst-not-references.stderr new file mode 100644 index 0000000..c0d9e0f --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-dst-not-references.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-mut-src-dst-not-references.rs:17:59 + | +17 | const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&mut _`, found `usize` + | expected due to this + | + = note: expected mutable reference `&mut _` + found type `usize` +help: consider mutably borrowing here + | +17 | const SRC_DST_NOT_REFERENCES: &mut usize = transmute_mut!(&mut 0usize); + | ++++ diff --git a/tests/ui-stable/transmute-mut-src-dst-unsized.rs b/tests/ui-stable/transmute-mut-src-dst-unsized.rs new file mode 100644 index 0000000..1bebcf2 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting between unsized source and +// destination types. +const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); diff --git a/tests/ui-stable/transmute-mut-src-dst-unsized.stderr b/tests/ui-stable/transmute-mut-src-dst-unsized.stderr new file mode 100644 index 0000000..8cf7664 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-dst-unsized.stderr @@ -0,0 +1,288 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsFromBytes` + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertDstIsAsBytes` + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertDstIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-dst-unsized.rs:17:36 + | +17 | const SRC_DST_UNSIZED: &mut [u8] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-src-generic.rs b/tests/ui-stable/transmute-mut-src-generic.rs new file mode 100644 index 0000000..a3ef397 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_mut, AsBytes}; + +fn main() {} + +fn transmute_mut<T: AsBytes + FromBytes>(t: &mut T) -> &mut u8 { + // `transmute_mut!` requires the source type to be concrete. + transmute_mut!(t) +} diff --git a/tests/ui-stable/transmute-mut-src-generic.stderr b/tests/ui-stable/transmute-mut-src-generic.stderr new file mode 100644 index 0000000..fc4809e --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-generic.stderr @@ -0,0 +1,10 @@ +error[E0405]: cannot find trait `FromBytes` in this scope + --> tests/ui-stable/transmute-mut-src-generic.rs:15:31 + | +15 | fn transmute_mut<T: AsBytes + FromBytes>(t: &mut T) -> &mut u8 { + | ^^^^^^^^^ not found in this scope + | +help: consider importing this trait + | +11 + use zerocopy::FromBytes; + | diff --git a/tests/ui-stable/transmute-mut-src-immutable.rs b/tests/ui-stable/transmute-mut-src-immutable.rs new file mode 100644 index 0000000..08088d0 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-immutable.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +fn ref_src_immutable() { + // `transmute_mut!` requires that its source type be a mutable reference. + let _: &mut u8 = transmute_mut!(&0u8); +} diff --git a/tests/ui-stable/transmute-mut-src-immutable.stderr b/tests/ui-stable/transmute-mut-src-immutable.stderr new file mode 100644 index 0000000..0115c79 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-immutable.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-mut-src-immutable.rs:17:37 + | +17 | let _: &mut u8 = transmute_mut!(&0u8); + | ---------------^^^^- + | | | + | | types differ in mutability + | expected due to this + | + = note: expected mutable reference `&mut _` + found reference `&u8` diff --git a/tests/ui-stable/transmute-mut-src-not-a-reference.rs b/tests/ui-stable/transmute-mut-src-not-a-reference.rs new file mode 100644 index 0000000..bf8bc32 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from a non-reference source +// type. +const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(0usize); diff --git a/tests/ui-stable/transmute-mut-src-not-a-reference.stderr b/tests/ui-stable/transmute-mut-src-not-a-reference.stderr new file mode 100644 index 0000000..8c1d9b4 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-not-a-reference.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-mut-src-not-a-reference.rs:17:53 + | +17 | const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&mut _`, found `usize` + | expected due to this + | + = note: expected mutable reference `&mut _` + found type `usize` +help: consider mutably borrowing here + | +17 | const SRC_NOT_A_REFERENCE: &mut u8 = transmute_mut!(&mut 0usize); + | ++++ diff --git a/tests/ui-stable/transmute-mut-src-not-asbytes.rs b/tests/ui-stable/transmute-mut-src-not-asbytes.rs new file mode 100644 index 0000000..6a14f12 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-not-asbytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-stable/transmute-mut-src-not-asbytes.stderr b/tests/ui-stable/transmute-mut-src-not-asbytes.stderr new file mode 100644 index 0000000..9fc9546 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-not-asbytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `Src: AsBytes` is not satisfied + --> tests/ui-stable/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `Src` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-stable/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Src: AsBytes` is not satisfied + --> tests/ui-stable/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `Src` + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-stable/transmute-mut-src-not-asbytes.rs:24:36 + | +24 | const SRC_NOT_AS_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-src-not-frombytes.rs b/tests/ui-stable/transmute-mut-src-not-frombytes.rs new file mode 100644 index 0000000..2ebe036 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-not-frombytes.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +#[derive(zerocopy::AsBytes)] +#[repr(C)] +struct Src; + +#[derive(zerocopy::FromZeroes, zerocopy::FromBytes, zerocopy::AsBytes)] +#[repr(C)] +struct Dst; + +// `transmute_mut` requires that the source type implements `FromBytes` +const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); diff --git a/tests/ui-stable/transmute-mut-src-not-frombytes.stderr b/tests/ui-stable/transmute-mut-src-not-frombytes.stderr new file mode 100644 index 0000000..cc4a19d --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-not-frombytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `Src: FromBytes` is not satisfied + --> tests/ui-stable/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `Src` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-stable/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Src: FromBytes` is not satisfied + --> tests/ui-stable/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `Src` + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-stable/transmute-mut-src-not-frombytes.rs:24:38 + | +24 | const SRC_NOT_FROM_BYTES: &mut Dst = transmute_mut!(&mut Src); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-mut-src-unsized.rs b/tests/ui-stable/transmute-mut-src-unsized.rs new file mode 100644 index 0000000..413dd68 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-unsized.rs @@ -0,0 +1,16 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_mut; + +fn main() {} + +// `transmute_mut!` does not support transmuting from an unsized source type. +const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); diff --git a/tests/ui-stable/transmute-mut-src-unsized.stderr b/tests/ui-stable/transmute-mut-src-unsized.stderr new file mode 100644 index 0000000..7f6def9 --- /dev/null +++ b/tests/ui-stable/transmute-mut-src-unsized.stderr @@ -0,0 +1,195 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsFromBytes` + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsFromBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertSrcIsAsBytes` + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertSrcIsAsBytes` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-mut-src-unsized.rs:16:35 + | +16 | const SRC_UNSIZED: &mut [u8; 1] = transmute_mut!(&mut [0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_mut` + --> src/macro_util.rs + | + | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_mut` + = note: this error originates in the macro `transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ptr-to-usize.rs b/tests/ui-stable/transmute-ptr-to-usize.rs new file mode 100644 index 0000000..5af8859 --- /dev/null +++ b/tests/ui-stable/transmute-ptr-to-usize.rs @@ -0,0 +1,20 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// It is unclear whether we can or should support this transmutation, especially +// in a const context. This test ensures that even if such a transmutation +// becomes valid due to the requisite implementations of `FromBytes` being +// added, that we re-examine whether it should specifically be valid in a const +// context. +const POINTER_VALUE: usize = transmute!(&0usize as *const usize); diff --git a/tests/ui-stable/transmute-ptr-to-usize.stderr b/tests/ui-stable/transmute-ptr-to-usize.stderr new file mode 100644 index 0000000..4f4d583 --- /dev/null +++ b/tests/ui-stable/transmute-ptr-to-usize.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied + --> tests/ui-stable/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `*const usize` + | required by a bound introduced by this call + | + = help: the trait `AsBytes` is implemented for `usize` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied + --> tests/ui-stable/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `*const usize` + | + = help: the trait `AsBytes` is implemented for `usize` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ptr-to-usize.rs:20:30 + | +20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-alignment-increase.rs b/tests/ui-stable/transmute-ref-alignment-increase.rs new file mode 100644 index 0000000..bf1988c --- /dev/null +++ b/tests/ui-stable/transmute-ref-alignment-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a type of smaller +// alignment to one of larger alignment. +const INCREASE_ALIGNMENT: &AU16 = transmute_ref!(&[0u8; 2]); diff --git a/tests/ui-stable/transmute-ref-alignment-increase.stderr b/tests/ui-stable/transmute-ref-alignment-increase.stderr new file mode 100644 index 0000000..a34c406 --- /dev/null +++ b/tests/ui-stable/transmute-ref-alignment-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-alignment-increase.rs:19:35 + | +19 | const INCREASE_ALIGNMENT: &AU16 = transmute_ref!(&[0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<[u8; 2]>` (8 bits) + = note: target type: `MaxAlignsOf<[u8; 2], AU16>` (16 bits) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-dst-generic.rs b/tests/ui-stable/transmute-ref-dst-generic.rs new file mode 100644 index 0000000..bf4a0f9 --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, FromBytes}; + +fn main() {} + +fn transmute_ref<T: FromBytes>(u: &u8) -> &T { + // `transmute_ref!` requires the destination type to be concrete. + transmute_ref!(u) +} diff --git a/tests/ui-stable/transmute-ref-dst-generic.stderr b/tests/ui-stable/transmute-ref-dst-generic.stderr new file mode 100644 index 0000000..e30b9f6 --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-dst-generic.rs:17:5 + | +17 | transmute_ref!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `T` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-dst-generic.rs:17:5 + | +17 | transmute_ref!(u) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<u8>` (8 bits) + = note: target type: `MaxAlignsOf<u8, T>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-dst-mutable.rs b/tests/ui-stable/transmute-ref-dst-mutable.rs new file mode 100644 index 0000000..fa0e6e4 --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-mutable.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +fn ref_dst_mutable() { + // `transmute_ref!` requires that its destination type be an immutable + // reference. + let _: &mut u8 = transmute_ref!(&0u8); +} diff --git a/tests/ui-stable/transmute-ref-dst-mutable.stderr b/tests/ui-stable/transmute-ref-dst-mutable.stderr new file mode 100644 index 0000000..c70f6ea --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-mutable.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-dst-mutable.rs:18:22 + | +18 | let _: &mut u8 = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut u8` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-dst-not-a-reference.rs b/tests/ui-stable/transmute-ref-dst-not-a-reference.rs new file mode 100644 index 0000000..de55f9a --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting into a non-reference +// destination type. +const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); diff --git a/tests/ui-stable/transmute-ref-dst-not-a-reference.stderr b/tests/ui-stable/transmute-ref-dst-not-a-reference.stderr new file mode 100644 index 0000000..ab3f90c --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-not-a-reference.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-dst-not-a-reference.rs:17:36 + | +17 | const DST_NOT_A_REFERENCE: usize = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-dst-not-frombytes.rs b/tests/ui-stable/transmute-ref-dst-not-frombytes.rs new file mode 100644 index 0000000..d81f64d --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-not-frombytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref` requires that the destination type implements `FromBytes` +const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); diff --git a/tests/ui-stable/transmute-ref-dst-not-frombytes.stderr b/tests/ui-stable/transmute-ref-dst-not-frombytes.stderr new file mode 100644 index 0000000..76d18c5 --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-not-frombytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy: FromBytes` is not satisfied + --> tests/ui-stable/transmute-ref-dst-not-frombytes.rs:18:42 + | +18 | const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `FromBytes` is not implemented for `NotZerocopy` + | required by a bound introduced by this call + | + = help: the following other types implement trait `FromBytes`: + isize + i8 + i16 + i32 + i64 + i128 + usize + u8 + and $N others +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-stable/transmute-ref-dst-not-frombytes.rs:18:42 + | +18 | const DST_NOT_FROM_BYTES: &NotZerocopy = transmute_ref!(&AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-dst-unsized.rs b/tests/ui-stable/transmute-ref-dst-unsized.rs new file mode 100644 index 0000000..625f1fa --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting into an unsized destination +// type. +const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); diff --git a/tests/ui-stable/transmute-ref-dst-unsized.stderr b/tests/ui-stable/transmute-ref-dst-unsized.stderr new file mode 100644 index 0000000..8a0c761 --- /dev/null +++ b/tests/ui-stable/transmute-ref-dst-unsized.stderr @@ -0,0 +1,89 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-stable/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-dst-unsized.rs:17:28 + | +17 | const DST_UNSIZED: &[u8] = transmute_ref!(&[0u8; 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-illegal-lifetime.rs b/tests/ui-stable/transmute-ref-illegal-lifetime.rs new file mode 100644 index 0000000..8dd191e --- /dev/null +++ b/tests/ui-stable/transmute-ref-illegal-lifetime.rs @@ -0,0 +1,15 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +fn main() {} + +fn increase_lifetime() { + let x = 0u64; + // It is illegal to increase the lifetime scope. + let _: &'static u64 = zerocopy::transmute_ref!(&x); +} diff --git a/tests/ui-stable/transmute-ref-illegal-lifetime.stderr b/tests/ui-stable/transmute-ref-illegal-lifetime.stderr new file mode 100644 index 0000000..1ef34fe --- /dev/null +++ b/tests/ui-stable/transmute-ref-illegal-lifetime.stderr @@ -0,0 +1,12 @@ +error[E0597]: `x` does not live long enough + --> tests/ui-stable/transmute-ref-illegal-lifetime.rs:14:52 + | +12 | let x = 0u64; + | - binding `x` declared here +13 | // It is illegal to increase the lifetime scope. +14 | let _: &'static u64 = zerocopy::transmute_ref!(&x); + | ------------ ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +15 | } + | - `x` dropped here while still borrowed diff --git a/tests/ui-stable/transmute-ref-size-decrease.rs b/tests/ui-stable/transmute-ref-size-decrease.rs new file mode 100644 index 0000000..1d66a54 --- /dev/null +++ b/tests/ui-stable/transmute-ref-size-decrease.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// Although this is not a soundness requirement, we currently require that the +// size of the destination type is not smaller than the size of the source type. +const DECREASE_SIZE: &u8 = transmute_ref!(&[0u8; 2]); diff --git a/tests/ui-stable/transmute-ref-size-decrease.stderr b/tests/ui-stable/transmute-ref-size-decrease.stderr new file mode 100644 index 0000000..f353b26 --- /dev/null +++ b/tests/ui-stable/transmute-ref-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-size-decrease.rs:17:28 + | +17 | const DECREASE_SIZE: &u8 = transmute_ref!(&[0u8; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; 2]` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-size-increase.rs b/tests/ui-stable/transmute-ref-size-increase.rs new file mode 100644 index 0000000..cdca560 --- /dev/null +++ b/tests/ui-stable/transmute-ref-size-increase.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: &[u8; 2] = transmute_ref!(&0u8); diff --git a/tests/ui-stable/transmute-ref-size-increase.stderr b/tests/ui-stable/transmute-ref-size-increase.stderr new file mode 100644 index 0000000..f51eb63 --- /dev/null +++ b/tests/ui-stable/transmute-ref-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-size-increase.rs:17:33 + | +17 | const INCREASE_SIZE: &[u8; 2] = transmute_ref!(&0u8); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `[u8; 2]` (16 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-src-dst-generic.rs b/tests/ui-stable/transmute-ref-src-dst-generic.rs new file mode 100644 index 0000000..409d785 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-dst-generic.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, AsBytes, FromBytes}; + +fn main() {} + +fn transmute_ref<T: AsBytes, U: FromBytes>(t: &T) -> &U { + // `transmute_ref!` requires the source and destination types to be + // concrete. + transmute_ref!(t) +} diff --git a/tests/ui-stable/transmute-ref-src-dst-generic.stderr b/tests/ui-stable/transmute-ref-src-dst-generic.stderr new file mode 100644 index 0000000..0905dc6 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-dst-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-src-dst-generic.rs:18:5 + | +18 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `U` (this type does not have a fixed size) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-src-dst-generic.rs:18:5 + | +18 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, U>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-src-dst-not-references.rs b/tests/ui-stable/transmute-ref-src-dst-not-references.rs new file mode 100644 index 0000000..114e917 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-dst-not-references.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting between non-reference source +// and destination types. +const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); diff --git a/tests/ui-stable/transmute-ref-src-dst-not-references.stderr b/tests/ui-stable/transmute-ref-src-dst-not-references.stderr new file mode 100644 index 0000000..8a80e99 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-dst-not-references.stderr @@ -0,0 +1,45 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-src-dst-not-references.rs:17:54 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&_`, found `usize` + | expected due to this + | + = note: expected reference `&_` + found type `usize` +help: consider borrowing here + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(&0usize); + | + + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-src-dst-not-references.rs:17:39 + | +17 | const SRC_DST_NOT_REFERENCES: usize = transmute_ref!(0usize); + | ^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&_` + | + = note: expected type `usize` + found reference `&_` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-src-dst-unsized.rs b/tests/ui-stable/transmute-ref-src-dst-unsized.rs new file mode 100644 index 0000000..6bfe7ff --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-dst-unsized.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting between unsized source and +// destination types. +const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); diff --git a/tests/ui-stable/transmute-ref-src-dst-unsized.stderr b/tests/ui-stable/transmute-ref-src-dst-unsized.stderr new file mode 100644 index 0000000..ca62fcf --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-dst-unsized.stderr @@ -0,0 +1,240 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsFromBytes` + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-dst-unsized.rs:17:32 + | +17 | const SRC_DST_UNSIZED: &[u8] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-src-generic.rs b/tests/ui-stable/transmute-ref-src-generic.rs new file mode 100644 index 0000000..010281c --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-generic.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::{transmute_ref, AsBytes}; + +fn main() {} + +fn transmute_ref<T: AsBytes>(t: &T) -> &u8 { + // `transmute_ref!` requires the source type to be concrete. + transmute_ref!(t) +} diff --git a/tests/ui-stable/transmute-ref-src-generic.stderr b/tests/ui-stable/transmute-ref-src-generic.stderr new file mode 100644 index 0000000..b6bbd16 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-generic.stderr @@ -0,0 +1,19 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-src-generic.rs:17:5 + | +17 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-ref-src-generic.rs:17:5 + | +17 | transmute_ref!(t) + | ^^^^^^^^^^^^^^^^^ + | + = note: source type: `AlignOf<T>` (size can vary because of T) + = note: target type: `MaxAlignsOf<T, u8>` (size can vary because of T) + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-src-not-a-reference.rs b/tests/ui-stable/transmute-ref-src-not-a-reference.rs new file mode 100644 index 0000000..90661b3 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-not-a-reference.rs @@ -0,0 +1,17 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from a non-reference source +// type. +const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(0usize); diff --git a/tests/ui-stable/transmute-ref-src-not-a-reference.stderr b/tests/ui-stable/transmute-ref-src-not-a-reference.stderr new file mode 100644 index 0000000..622c3db --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-not-a-reference.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> tests/ui-stable/transmute-ref-src-not-a-reference.rs:17:49 + | +17 | const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(0usize); + | ---------------^^^^^^- + | | | + | | expected `&_`, found `usize` + | expected due to this + | + = note: expected reference `&_` + found type `usize` +help: consider borrowing here + | +17 | const SRC_NOT_A_REFERENCE: &u8 = transmute_ref!(&0usize); + | + diff --git a/tests/ui-stable/transmute-ref-src-not-asbytes.rs b/tests/ui-stable/transmute-ref-src-not-asbytes.rs new file mode 100644 index 0000000..6ab19f3 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-not-asbytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); diff --git a/tests/ui-stable/transmute-ref-src-not-asbytes.stderr b/tests/ui-stable/transmute-ref-src-not-asbytes.stderr new file mode 100644 index 0000000..2ded6ba --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-not-asbytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-stable/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-stable/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ref-src-not-asbytes.rs:18:33 + | +18 | const SRC_NOT_AS_BYTES: &AU16 = transmute_ref!(&NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ref-src-unsized.rs b/tests/ui-stable/transmute-ref-src-unsized.rs new file mode 100644 index 0000000..14e72b4 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-unsized.rs @@ -0,0 +1,16 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +extern crate zerocopy; + +use zerocopy::transmute_ref; + +fn main() {} + +// `transmute_ref!` does not support transmuting from an unsized source type. +const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); diff --git a/tests/ui-stable/transmute-ref-src-unsized.stderr b/tests/ui-stable/transmute-ref-src-unsized.stderr new file mode 100644 index 0000000..b194d67 --- /dev/null +++ b/tests/ui-stable/transmute-ref-src-unsized.stderr @@ -0,0 +1,164 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute` + --> $RUST/core/src/intrinsics.rs + | + | pub fn transmute<Src, Dst>(src: Src) -> Dst; + | ^^^ required by this bound in `transmute` + = note: this error originates in the macro `$crate::assert_size_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf::<T>::into_t` + --> src/macro_util.rs + | + | impl<T> AlignOf<T> { + | ^ required by this bound in `AlignOf::<T>::into_t` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn into_t(self) -> T { + | ------ required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: the left-hand-side of an assignment must have a statically known size + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf::<T, U>::new` + --> src/macro_util.rs + | + | impl<T, U> MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf::<T, U>::new` + | #[inline(never)] // Make `missing_inline_in_public_items` happy. + | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { + | --- required by a bound in this associated function + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `MaxAlignsOf` + --> src/macro_util.rs + | + | pub union MaxAlignsOf<T, U> { + | ^ required by this bound in `MaxAlignsOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `AlignOf` + --> src/macro_util.rs + | + | pub struct AlignOf<T> { + | ^ required by this bound in `AlignOf` + = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> tests/ui-stable/transmute-ref-src-unsized.rs:16:31 + | +16 | const SRC_UNSIZED: &[u8; 1] = transmute_ref!(&[0u8][..]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | doesn't have a size known at compile-time + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `transmute_ref` + --> src/macro_util.rs + | + | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( + | ^^^ required by this bound in `transmute_ref` + = note: this error originates in the macro `transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-size-decrease.rs b/tests/ui-stable/transmute-size-decrease.rs new file mode 100644 index 0000000..1d56831 --- /dev/null +++ b/tests/ui-stable/transmute-size-decrease.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// Although this is not a soundness requirement, we currently require that the +// size of the destination type is not smaller than the size of the source type. +const DECREASE_SIZE: u8 = transmute!(AU16(0)); diff --git a/tests/ui-stable/transmute-size-decrease.stderr b/tests/ui-stable/transmute-size-decrease.stderr new file mode 100644 index 0000000..0241662 --- /dev/null +++ b/tests/ui-stable/transmute-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-size-decrease.rs:19:27 + | +19 | const DECREASE_SIZE: u8 = transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AU16` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-size-increase.rs b/tests/ui-stable/transmute-size-increase.rs new file mode 100644 index 0000000..32f9363 --- /dev/null +++ b/tests/ui-stable/transmute-size-increase.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: AU16 = transmute!(0u8); diff --git a/tests/ui-stable/transmute-size-increase.stderr b/tests/ui-stable/transmute-size-increase.stderr new file mode 100644 index 0000000..87d82a2 --- /dev/null +++ b/tests/ui-stable/transmute-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-size-increase.rs:19:29 + | +19 | const INCREASE_SIZE: AU16 = transmute!(0u8); + | ^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `AU16` (16 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-src-not-asbytes.rs b/tests/ui-stable/transmute-src-not-asbytes.rs new file mode 100644 index 0000000..dd73021 --- /dev/null +++ b/tests/ui-stable/transmute-src-not-asbytes.rs @@ -0,0 +1,18 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 +// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/util.rs"); + +extern crate zerocopy; + +use zerocopy::transmute; + +fn main() {} + +// `transmute` requires that the source type implements `AsBytes` +const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); diff --git a/tests/ui-stable/transmute-src-not-asbytes.stderr b/tests/ui-stable/transmute-src-not-asbytes.stderr new file mode 100644 index 0000000..f2e834e --- /dev/null +++ b/tests/ui-stable/transmute-src-not-asbytes.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-stable/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | required by a bound introduced by this call + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy<AU16>: AsBytes` is not satisfied + --> tests/ui-stable/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `NotZerocopy<AU16>` + | + = help: the following other types implement trait `AsBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `AssertIsAsBytes` + --> tests/ui-stable/transmute-src-not-asbytes.rs:18:32 + | +18 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsAsBytes` + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) |