aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-02-02 23:56:36 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-02-02 23:56:36 +0000
commit10ffb6bbcf05dc0a71bbf29006b34c8a80d15bd0 (patch)
tree906f9dd3bae52323bc6fde373383247e3f67f65a
parenteaa766f14935c0f26afc7f8225143d1dc261b6d7 (diff)
parentadfecb30ba9dc20ed624a80576a1a5aaad967491 (diff)
downloadzerocopy-simpleperf-release.tar.gz
Snap for 11400057 from adfecb30ba9dc20ed624a80576a1a5aaad967491 to simpleperf-releasesimpleperf-release
Change-Id: I0297460faebc208229f3940b3fe7ee5178e79d00
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp12
-rw-r--r--CONTRIBUTING.md10
-rw-r--r--Cargo.toml32
-rw-r--r--Cargo.toml.orig37
-rw-r--r--INTERNAL.md17
-rw-r--r--LICENSE-APACHE202
-rw-r--r--LICENSE-BSD24
-rw-r--r--LICENSE-MIT26
-rw-r--r--METADATA23
-rw-r--r--POLICIES.md95
-rw-r--r--README.md63
-rwxr-xr-xcargo.sh52
-rw-r--r--cargo2rulesmk.json3
-rw-r--r--clippy.toml10
-rwxr-xr-xgenerate-readme.sh20
-rw-r--r--patches/rules.mk.diff14
-rw-r--r--rules.mk23
-rw-r--r--rustfmt.toml10
-rw-r--r--src/byteorder.rs414
-rw-r--r--src/derive_util.rs127
-rw-r--r--src/lib.rs5291
-rw-r--r--src/macro_util.rs670
-rw-r--r--src/macros.rs213
-rw-r--r--src/third_party/rust/LICENSE-APACHE176
-rw-r--r--src/third_party/rust/LICENSE-MIT23
-rw-r--r--src/third_party/rust/README.fuchsia7
-rw-r--r--src/third_party/rust/layout.rs45
-rw-r--r--src/util.rs721
-rw-r--r--src/wrappers.rs36
-rw-r--r--testdata/include_value/data1
-rw-r--r--tests/trybuild.rs48
-rw-r--r--tests/ui-msrv/include_value_not_from_bytes.rs12
-rw-r--r--tests/ui-msrv/include_value_not_from_bytes.stderr12
-rw-r--r--tests/ui-msrv/include_value_wrong_size.rs (renamed from tests/ui-stable/transmute-illegal.rs)5
-rw-r--r--tests/ui-msrv/include_value_wrong_size.stderr9
-rw-r--r--tests/ui-msrv/invalid-impls/invalid-impls.rs10
-rw-r--r--tests/ui-msrv/invalid-impls/invalid-impls.stderr72
-rw-r--r--tests/ui-msrv/max-align.rs99
-rw-r--r--tests/ui-msrv/max-align.stderr5
-rw-r--r--tests/ui-msrv/transmute-dst-not-frombytes.rs18
-rw-r--r--tests/ui-msrv/transmute-dst-not-frombytes.stderr12
-rw-r--r--tests/ui-msrv/transmute-illegal.stderr18
-rw-r--r--tests/ui-msrv/transmute-mut-alignment-increase.rs19
-rw-r--r--tests/ui-msrv/transmute-mut-alignment-increase.stderr36
-rw-r--r--tests/ui-msrv/transmute-mut-const.rs20
-rw-r--r--tests/ui-msrv/transmute-mut-const.stderr41
-rw-r--r--tests/ui-msrv/transmute-mut-dst-generic.rs18
-rw-r--r--tests/ui-msrv/transmute-mut-dst-generic.stderr19
-rw-r--r--tests/ui-msrv/transmute-mut-dst-not-a-reference.rs17
-rw-r--r--tests/ui-msrv/transmute-mut-dst-not-a-reference.stderr39
-rw-r--r--tests/ui-msrv/transmute-mut-dst-not-asbytes.rs24
-rw-r--r--tests/ui-msrv/transmute-mut-dst-not-asbytes.stderr12
-rw-r--r--tests/ui-msrv/transmute-mut-dst-not-frombytes.rs24
-rw-r--r--tests/ui-msrv/transmute-mut-dst-not-frombytes.stderr12
-rw-r--r--tests/ui-msrv/transmute-mut-dst-unsized.rs17
-rw-r--r--tests/ui-msrv/transmute-mut-dst-unsized.stderr108
-rw-r--r--tests/ui-msrv/transmute-mut-illegal-lifetime.rs15
-rw-r--r--tests/ui-msrv/transmute-mut-illegal-lifetime.stderr9
-rw-r--r--tests/ui-msrv/transmute-mut-size-decrease.rs17
-rw-r--r--tests/ui-msrv/transmute-mut-size-decrease.stderr36
-rw-r--r--tests/ui-msrv/transmute-mut-size-increase.rs17
-rw-r--r--tests/ui-msrv/transmute-mut-size-increase.stderr36
-rw-r--r--tests/ui-msrv/transmute-mut-src-dst-generic.rs19
-rw-r--r--tests/ui-msrv/transmute-mut-src-dst-generic.stderr19
-rw-r--r--tests/ui-msrv/transmute-mut-src-dst-not-references.rs17
-rw-r--r--tests/ui-msrv/transmute-mut-src-dst-not-references.stderr12
-rw-r--r--tests/ui-msrv/transmute-mut-src-dst-unsized.rs17
-rw-r--r--tests/ui-msrv/transmute-mut-src-dst-unsized.stderr237
-rw-r--r--tests/ui-msrv/transmute-mut-src-generic.rs18
-rw-r--r--tests/ui-msrv/transmute-mut-src-generic.stderr10
-rw-r--r--tests/ui-msrv/transmute-mut-src-immutable.rs18
-rw-r--r--tests/ui-msrv/transmute-mut-src-immutable.stderr11
-rw-r--r--tests/ui-msrv/transmute-mut-src-not-a-reference.rs17
-rw-r--r--tests/ui-msrv/transmute-mut-src-not-a-reference.stderr12
-rw-r--r--tests/ui-msrv/transmute-mut-src-not-asbytes.rs24
-rw-r--r--tests/ui-msrv/transmute-mut-src-not-asbytes.stderr25
-rw-r--r--tests/ui-msrv/transmute-mut-src-not-frombytes.rs24
-rw-r--r--tests/ui-msrv/transmute-mut-src-not-frombytes.stderr25
-rw-r--r--tests/ui-msrv/transmute-mut-src-unsized.rs16
-rw-r--r--tests/ui-msrv/transmute-mut-src-unsized.stderr198
-rw-r--r--tests/ui-msrv/transmute-ptr-to-usize.rs20
-rw-r--r--tests/ui-msrv/transmute-ptr-to-usize.stderr37
-rw-r--r--tests/ui-msrv/transmute-ref-alignment-increase.rs19
-rw-r--r--tests/ui-msrv/transmute-ref-alignment-increase.stderr9
-rw-r--r--tests/ui-msrv/transmute-ref-dst-generic.rs18
-rw-r--r--tests/ui-msrv/transmute-ref-dst-generic.stderr19
-rw-r--r--tests/ui-msrv/transmute-ref-dst-mutable.rs19
-rw-r--r--tests/ui-msrv/transmute-ref-dst-mutable.stderr29
-rw-r--r--tests/ui-msrv/transmute-ref-dst-not-a-reference.rs17
-rw-r--r--tests/ui-msrv/transmute-ref-dst-not-a-reference.stderr29
-rw-r--r--tests/ui-msrv/transmute-ref-dst-not-frombytes.rs18
-rw-r--r--tests/ui-msrv/transmute-ref-dst-not-frombytes.stderr12
-rw-r--r--tests/ui-msrv/transmute-ref-dst-unsized.rs17
-rw-r--r--tests/ui-msrv/transmute-ref-dst-unsized.stderr94
-rw-r--r--tests/ui-msrv/transmute-ref-illegal-lifetime.rs15
-rw-r--r--tests/ui-msrv/transmute-ref-illegal-lifetime.stderr9
-rw-r--r--tests/ui-msrv/transmute-ref-size-decrease.rs17
-rw-r--r--tests/ui-msrv/transmute-ref-size-decrease.stderr9
-rw-r--r--tests/ui-msrv/transmute-ref-size-increase.rs17
-rw-r--r--tests/ui-msrv/transmute-ref-size-increase.stderr9
-rw-r--r--tests/ui-msrv/transmute-ref-src-dst-generic.rs19
-rw-r--r--tests/ui-msrv/transmute-ref-src-dst-generic.stderr19
-rw-r--r--tests/ui-msrv/transmute-ref-src-dst-not-references.rs17
-rw-r--r--tests/ui-msrv/transmute-ref-src-dst-not-references.stderr42
-rw-r--r--tests/ui-msrv/transmute-ref-src-dst-unsized.rs17
-rw-r--r--tests/ui-msrv/transmute-ref-src-dst-unsized.stderr195
-rw-r--r--tests/ui-msrv/transmute-ref-src-generic.rs18
-rw-r--r--tests/ui-msrv/transmute-ref-src-generic.stderr19
-rw-r--r--tests/ui-msrv/transmute-ref-src-not-a-reference.rs17
-rw-r--r--tests/ui-msrv/transmute-ref-src-not-a-reference.stderr12
-rw-r--r--tests/ui-msrv/transmute-ref-src-not-asbytes.rs18
-rw-r--r--tests/ui-msrv/transmute-ref-src-not-asbytes.stderr25
-rw-r--r--tests/ui-msrv/transmute-ref-src-unsized.rs16
-rw-r--r--tests/ui-msrv/transmute-ref-src-unsized.stderr170
-rw-r--r--tests/ui-msrv/transmute-size-decrease.rs19
-rw-r--r--tests/ui-msrv/transmute-size-decrease.stderr9
-rw-r--r--tests/ui-msrv/transmute-size-increase.rs19
-rw-r--r--tests/ui-msrv/transmute-size-increase.stderr9
-rw-r--r--tests/ui-msrv/transmute-src-not-asbytes.rs18
-rw-r--r--tests/ui-msrv/transmute-src-not-asbytes.stderr25
-rw-r--r--tests/ui-nightly/include_value_not_from_bytes.rs12
-rw-r--r--tests/ui-nightly/include_value_not_from_bytes.stderr25
-rw-r--r--tests/ui-nightly/include_value_wrong_size.rs (renamed from tests/ui-msrv/transmute-illegal.rs)5
-rw-r--r--tests/ui-nightly/include_value_wrong_size.stderr9
-rw-r--r--tests/ui-nightly/invalid-impls/invalid-impls.rs10
-rw-r--r--tests/ui-nightly/invalid-impls/invalid-impls.stderr72
-rw-r--r--tests/ui-nightly/max-align.rs99
-rw-r--r--tests/ui-nightly/max-align.stderr5
-rw-r--r--tests/ui-nightly/transmute-dst-not-frombytes.rs18
-rw-r--r--tests/ui-nightly/transmute-dst-not-frombytes.stderr25
-rw-r--r--tests/ui-nightly/transmute-illegal.stderr16
-rw-r--r--tests/ui-nightly/transmute-mut-alignment-increase.rs19
-rw-r--r--tests/ui-nightly/transmute-mut-alignment-increase.stderr9
-rw-r--r--tests/ui-nightly/transmute-mut-const.rs20
-rw-r--r--tests/ui-nightly/transmute-mut-const.stderr42
-rw-r--r--tests/ui-nightly/transmute-mut-dst-generic.rs18
-rw-r--r--tests/ui-nightly/transmute-mut-dst-generic.stderr19
-rw-r--r--tests/ui-nightly/transmute-mut-dst-not-a-reference.rs17
-rw-r--r--tests/ui-nightly/transmute-mut-dst-not-a-reference.stderr39
-rw-r--r--tests/ui-nightly/transmute-mut-dst-not-asbytes.rs24
-rw-r--r--tests/ui-nightly/transmute-mut-dst-not-asbytes.stderr25
-rw-r--r--tests/ui-nightly/transmute-mut-dst-not-frombytes.rs24
-rw-r--r--tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr25
-rw-r--r--tests/ui-nightly/transmute-mut-dst-unsized.rs17
-rw-r--r--tests/ui-nightly/transmute-mut-dst-unsized.stderr86
-rw-r--r--tests/ui-nightly/transmute-mut-illegal-lifetime.rs15
-rw-r--r--tests/ui-nightly/transmute-mut-illegal-lifetime.stderr12
-rw-r--r--tests/ui-nightly/transmute-mut-size-decrease.rs17
-rw-r--r--tests/ui-nightly/transmute-mut-size-decrease.stderr9
-rw-r--r--tests/ui-nightly/transmute-mut-size-increase.rs17
-rw-r--r--tests/ui-nightly/transmute-mut-size-increase.stderr9
-rw-r--r--tests/ui-nightly/transmute-mut-src-dst-generic.rs19
-rw-r--r--tests/ui-nightly/transmute-mut-src-dst-generic.stderr19
-rw-r--r--tests/ui-nightly/transmute-mut-src-dst-not-references.rs17
-rw-r--r--tests/ui-nightly/transmute-mut-src-dst-not-references.stderr15
-rw-r--r--tests/ui-nightly/transmute-mut-src-dst-unsized.rs17
-rw-r--r--tests/ui-nightly/transmute-mut-src-dst-unsized.stderr231
-rw-r--r--tests/ui-nightly/transmute-mut-src-generic.rs18
-rw-r--r--tests/ui-nightly/transmute-mut-src-generic.stderr10
-rw-r--r--tests/ui-nightly/transmute-mut-src-immutable.rs18
-rw-r--r--tests/ui-nightly/transmute-mut-src-immutable.stderr11
-rw-r--r--tests/ui-nightly/transmute-mut-src-not-a-reference.rs17
-rw-r--r--tests/ui-nightly/transmute-mut-src-not-a-reference.stderr15
-rw-r--r--tests/ui-nightly/transmute-mut-src-not-asbytes.rs24
-rw-r--r--tests/ui-nightly/transmute-mut-src-not-asbytes.stderr48
-rw-r--r--tests/ui-nightly/transmute-mut-src-not-frombytes.rs24
-rw-r--r--tests/ui-nightly/transmute-mut-src-not-frombytes.stderr48
-rw-r--r--tests/ui-nightly/transmute-mut-src-unsized.rs16
-rw-r--r--tests/ui-nightly/transmute-mut-src-unsized.stderr158
-rw-r--r--tests/ui-nightly/transmute-ptr-to-usize.rs20
-rw-r--r--tests/ui-nightly/transmute-ptr-to-usize.stderr30
-rw-r--r--tests/ui-nightly/transmute-ref-alignment-increase.rs19
-rw-r--r--tests/ui-nightly/transmute-ref-alignment-increase.stderr9
-rw-r--r--tests/ui-nightly/transmute-ref-dst-generic.rs18
-rw-r--r--tests/ui-nightly/transmute-ref-dst-generic.stderr19
-rw-r--r--tests/ui-nightly/transmute-ref-dst-mutable.rs19
-rw-r--r--tests/ui-nightly/transmute-ref-dst-mutable.stderr29
-rw-r--r--tests/ui-nightly/transmute-ref-dst-not-a-reference.rs17
-rw-r--r--tests/ui-nightly/transmute-ref-dst-not-a-reference.stderr29
-rw-r--r--tests/ui-nightly/transmute-ref-dst-not-frombytes.rs18
-rw-r--r--tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr25
-rw-r--r--tests/ui-nightly/transmute-ref-dst-unsized.rs17
-rw-r--r--tests/ui-nightly/transmute-ref-dst-unsized.stderr69
-rw-r--r--tests/ui-nightly/transmute-ref-illegal-lifetime.rs15
-rw-r--r--tests/ui-nightly/transmute-ref-illegal-lifetime.stderr12
-rw-r--r--tests/ui-nightly/transmute-ref-size-decrease.rs17
-rw-r--r--tests/ui-nightly/transmute-ref-size-decrease.stderr9
-rw-r--r--tests/ui-nightly/transmute-ref-size-increase.rs17
-rw-r--r--tests/ui-nightly/transmute-ref-size-increase.stderr9
-rw-r--r--tests/ui-nightly/transmute-ref-src-dst-generic.rs19
-rw-r--r--tests/ui-nightly/transmute-ref-src-dst-generic.stderr19
-rw-r--r--tests/ui-nightly/transmute-ref-src-dst-not-references.rs17
-rw-r--r--tests/ui-nightly/transmute-ref-src-dst-not-references.stderr45
-rw-r--r--tests/ui-nightly/transmute-ref-src-dst-unsized.rs17
-rw-r--r--tests/ui-nightly/transmute-ref-src-dst-unsized.stderr183
-rw-r--r--tests/ui-nightly/transmute-ref-src-generic.rs18
-rw-r--r--tests/ui-nightly/transmute-ref-src-generic.stderr19
-rw-r--r--tests/ui-nightly/transmute-ref-src-not-a-reference.rs17
-rw-r--r--tests/ui-nightly/transmute-ref-src-not-a-reference.stderr15
-rw-r--r--tests/ui-nightly/transmute-ref-src-not-asbytes.rs18
-rw-r--r--tests/ui-nightly/transmute-ref-src-not-asbytes.stderr48
-rw-r--r--tests/ui-nightly/transmute-ref-src-unsized.rs16
-rw-r--r--tests/ui-nightly/transmute-ref-src-unsized.stderr127
-rw-r--r--tests/ui-nightly/transmute-size-decrease.rs19
-rw-r--r--tests/ui-nightly/transmute-size-decrease.stderr9
-rw-r--r--tests/ui-nightly/transmute-size-increase.rs19
-rw-r--r--tests/ui-nightly/transmute-size-increase.stderr9
-rw-r--r--tests/ui-nightly/transmute-src-not-asbytes.rs18
-rw-r--r--tests/ui-nightly/transmute-src-not-asbytes.stderr48
-rw-r--r--tests/ui-stable/include_value_not_from_bytes.rs12
-rw-r--r--tests/ui-stable/include_value_not_from_bytes.stderr25
-rw-r--r--tests/ui-stable/include_value_wrong_size.rs (renamed from tests/ui-nightly/transmute-illegal.rs)5
-rw-r--r--tests/ui-stable/include_value_wrong_size.stderr9
-rw-r--r--tests/ui-stable/invalid-impls/invalid-impls.rs10
-rw-r--r--tests/ui-stable/invalid-impls/invalid-impls.stderr72
-rw-r--r--tests/ui-stable/max-align.rs99
-rw-r--r--tests/ui-stable/max-align.stderr5
-rw-r--r--tests/ui-stable/transmute-dst-not-frombytes.rs18
-rw-r--r--tests/ui-stable/transmute-dst-not-frombytes.stderr25
-rw-r--r--tests/ui-stable/transmute-illegal.stderr16
-rw-r--r--tests/ui-stable/transmute-mut-alignment-increase.rs19
-rw-r--r--tests/ui-stable/transmute-mut-alignment-increase.stderr9
-rw-r--r--tests/ui-stable/transmute-mut-const.rs20
-rw-r--r--tests/ui-stable/transmute-mut-const.stderr41
-rw-r--r--tests/ui-stable/transmute-mut-dst-generic.rs18
-rw-r--r--tests/ui-stable/transmute-mut-dst-generic.stderr19
-rw-r--r--tests/ui-stable/transmute-mut-dst-not-a-reference.rs17
-rw-r--r--tests/ui-stable/transmute-mut-dst-not-a-reference.stderr39
-rw-r--r--tests/ui-stable/transmute-mut-dst-not-asbytes.rs24
-rw-r--r--tests/ui-stable/transmute-mut-dst-not-asbytes.stderr25
-rw-r--r--tests/ui-stable/transmute-mut-dst-not-frombytes.rs24
-rw-r--r--tests/ui-stable/transmute-mut-dst-not-frombytes.stderr25
-rw-r--r--tests/ui-stable/transmute-mut-dst-unsized.rs17
-rw-r--r--tests/ui-stable/transmute-mut-dst-unsized.stderr106
-rw-r--r--tests/ui-stable/transmute-mut-illegal-lifetime.rs15
-rw-r--r--tests/ui-stable/transmute-mut-illegal-lifetime.stderr12
-rw-r--r--tests/ui-stable/transmute-mut-size-decrease.rs17
-rw-r--r--tests/ui-stable/transmute-mut-size-decrease.stderr9
-rw-r--r--tests/ui-stable/transmute-mut-size-increase.rs17
-rw-r--r--tests/ui-stable/transmute-mut-size-increase.stderr9
-rw-r--r--tests/ui-stable/transmute-mut-src-dst-generic.rs19
-rw-r--r--tests/ui-stable/transmute-mut-src-dst-generic.stderr19
-rw-r--r--tests/ui-stable/transmute-mut-src-dst-not-references.rs17
-rw-r--r--tests/ui-stable/transmute-mut-src-dst-not-references.stderr15
-rw-r--r--tests/ui-stable/transmute-mut-src-dst-unsized.rs17
-rw-r--r--tests/ui-stable/transmute-mut-src-dst-unsized.stderr288
-rw-r--r--tests/ui-stable/transmute-mut-src-generic.rs18
-rw-r--r--tests/ui-stable/transmute-mut-src-generic.stderr10
-rw-r--r--tests/ui-stable/transmute-mut-src-immutable.rs18
-rw-r--r--tests/ui-stable/transmute-mut-src-immutable.stderr11
-rw-r--r--tests/ui-stable/transmute-mut-src-not-a-reference.rs17
-rw-r--r--tests/ui-stable/transmute-mut-src-not-a-reference.stderr15
-rw-r--r--tests/ui-stable/transmute-mut-src-not-asbytes.rs24
-rw-r--r--tests/ui-stable/transmute-mut-src-not-asbytes.stderr48
-rw-r--r--tests/ui-stable/transmute-mut-src-not-frombytes.rs24
-rw-r--r--tests/ui-stable/transmute-mut-src-not-frombytes.stderr48
-rw-r--r--tests/ui-stable/transmute-mut-src-unsized.rs16
-rw-r--r--tests/ui-stable/transmute-mut-src-unsized.stderr195
-rw-r--r--tests/ui-stable/transmute-ptr-to-usize.rs20
-rw-r--r--tests/ui-stable/transmute-ptr-to-usize.stderr30
-rw-r--r--tests/ui-stable/transmute-ref-alignment-increase.rs19
-rw-r--r--tests/ui-stable/transmute-ref-alignment-increase.stderr9
-rw-r--r--tests/ui-stable/transmute-ref-dst-generic.rs18
-rw-r--r--tests/ui-stable/transmute-ref-dst-generic.stderr19
-rw-r--r--tests/ui-stable/transmute-ref-dst-mutable.rs19
-rw-r--r--tests/ui-stable/transmute-ref-dst-mutable.stderr29
-rw-r--r--tests/ui-stable/transmute-ref-dst-not-a-reference.rs17
-rw-r--r--tests/ui-stable/transmute-ref-dst-not-a-reference.stderr29
-rw-r--r--tests/ui-stable/transmute-ref-dst-not-frombytes.rs18
-rw-r--r--tests/ui-stable/transmute-ref-dst-not-frombytes.stderr25
-rw-r--r--tests/ui-stable/transmute-ref-dst-unsized.rs17
-rw-r--r--tests/ui-stable/transmute-ref-dst-unsized.stderr89
-rw-r--r--tests/ui-stable/transmute-ref-illegal-lifetime.rs15
-rw-r--r--tests/ui-stable/transmute-ref-illegal-lifetime.stderr12
-rw-r--r--tests/ui-stable/transmute-ref-size-decrease.rs17
-rw-r--r--tests/ui-stable/transmute-ref-size-decrease.stderr9
-rw-r--r--tests/ui-stable/transmute-ref-size-increase.rs17
-rw-r--r--tests/ui-stable/transmute-ref-size-increase.stderr9
-rw-r--r--tests/ui-stable/transmute-ref-src-dst-generic.rs19
-rw-r--r--tests/ui-stable/transmute-ref-src-dst-generic.stderr19
-rw-r--r--tests/ui-stable/transmute-ref-src-dst-not-references.rs17
-rw-r--r--tests/ui-stable/transmute-ref-src-dst-not-references.stderr45
-rw-r--r--tests/ui-stable/transmute-ref-src-dst-unsized.rs17
-rw-r--r--tests/ui-stable/transmute-ref-src-dst-unsized.stderr240
-rw-r--r--tests/ui-stable/transmute-ref-src-generic.rs18
-rw-r--r--tests/ui-stable/transmute-ref-src-generic.stderr19
-rw-r--r--tests/ui-stable/transmute-ref-src-not-a-reference.rs17
-rw-r--r--tests/ui-stable/transmute-ref-src-not-a-reference.stderr15
-rw-r--r--tests/ui-stable/transmute-ref-src-not-asbytes.rs18
-rw-r--r--tests/ui-stable/transmute-ref-src-not-asbytes.stderr48
-rw-r--r--tests/ui-stable/transmute-ref-src-unsized.rs16
-rw-r--r--tests/ui-stable/transmute-ref-src-unsized.stderr164
-rw-r--r--tests/ui-stable/transmute-size-decrease.rs19
-rw-r--r--tests/ui-stable/transmute-size-decrease.stderr9
-rw-r--r--tests/ui-stable/transmute-size-increase.rs19
-rw-r--r--tests/ui-stable/transmute-size-increase.stderr9
-rw-r--r--tests/ui-stable/transmute-src-not-asbytes.rs18
-rw-r--r--tests/ui-stable/transmute-src-not-asbytes.stderr48
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
diff --git a/Android.bp b/Android.bp
index b632469..0190d67 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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
diff --git a/Cargo.toml b/Cargo.toml
index 802ec0b..c4b1c9d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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.
+
diff --git a/METADATA b/METADATA
index 2b18dc0..762e3de 100644
--- a/METADATA
+++ b/METADATA
@@ -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).
diff --git a/README.md b/README.md
index d9d3d90..3d423bd 100644
--- a/README.md
+++ b/README.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.
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.
diff --git a/cargo.sh b/cargo.sh
index 7dacf84..f72e898 100755
--- a/cargo.sh
+++ b/cargo.sh
@@ -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);
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 3937911..cfa510e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)