unsafe_fields/lib.rs
1// Copyright 2024 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9// After updating the following doc comment, make sure to run the following
10// command to update `README.md` based on its contents:
11//
12// cargo -q run --manifest-path ../tools/Cargo.toml -p generate-readme > README.md
13
14//! Support for unsafe fields.
15//!
16//! This crate provides the [`unsafe_fields!`] macro, which can be used to mark
17//! fields as unsafe. Unsafe fields automatically have their types wrapped using
18//! the [`Unsafe`] wrapper type. An `Unsafe` is intended to be used to for
19//! struct, enum, or union fields which carry safety invariants. All accessors
20//! are `unsafe`, which requires any use of an `Unsafe` field to be inside an
21//! `unsafe` block. One exception is [`Unsafe::as_ref`], which is available when
22//! the `zerocopy_0_8` feature is enabled. See its docs for more information.
23//!
24//! An unsafe field has the type `Unsafe<O, F, const NAME_HASH: u128>`. `O` is
25//! the enclosing type (struct, enum, or union), `F` is the type of the field,
26//! and `NAME_HASH` is the hash of the field's name. `O` prevents swapping
27//! unsafe fields of the same `F` type between different enclosing types, and
28//! `NAME_HASH` prevents swapping different fields of the same `F` type within
29//! the same enclosing type. Note that swapping the same field between instances
30//! of the same type [cannot be prevented](crate#limitations).
31//!
32//! [immutable]: zerocopy_0_8::Immutable
33//!
34//! # Examples
35//!
36//! ```rust
37//! use unsafe_fields::{unsafe_fields, Unsafe};
38//!
39//! unsafe_fields! {
40//! /// A `usize` which is guaranteed to be even.
41//! pub struct EvenUsize {
42//! // INVARIANT: `n` is even.
43//! #[unsafe]
44//! n: usize,
45//! }
46//! }
47//!
48//! impl EvenUsize {
49//! /// Constructs a new `EvenUsize`.
50//! ///
51//! /// Returns `None` if `n` is odd.
52//! pub fn new(n: usize) -> Option<EvenUsize> {
53//! if n % 2 != 0 {
54//! return None;
55//! }
56//! // SAFETY: We just confirmed that `n` is even.
57//! let n = unsafe { Unsafe::new(n) };
58//! Some(EvenUsize { n })
59//! }
60//! }
61//! ```
62//!
63//! Attempting to swap unsafe fields of the same type is prevented:
64//!
65//! ```rust,compile_fail,E0308
66//! use unsafe_fields::{unsafe_fields, Unsafe};
67//!
68//! unsafe_fields! {
69//! /// A range.
70//! pub struct Range {
71//! // INVARIANT: `lo <= hi`.
72//! #[unsafe]
73//! lo: usize,
74//! #[unsafe]
75//! hi: usize,
76//! }
77//! }
78//!
79//! impl Range {
80//! pub fn swap(&mut self) {
81//! // ERROR: Mismatched types
82//! core::mem::swap(&mut self.lo, &mut self.hi);
83//! }
84//! }
85//! ```
86//!
87//! # Limitations
88//!
89//! Note that we cannot prevent `Unsafe`s from being swapped between the same
90//! field in instances of the same type:
91//!
92//! ```rust
93//! use unsafe_fields::{unsafe_fields, Unsafe};
94//!
95//! unsafe_fields! {
96//! /// A `usize` which is guaranteed to be even.
97//! pub struct EvenUsize {
98//! // INVARIANT: `n` is even.
99//! #[unsafe]
100//! n: usize,
101//! }
102//! }
103//!
104//! pub fn swap(a: &mut EvenUsize, b: &mut EvenUsize) {
105//! core::mem::swap(&mut a.n, &mut b.n);
106//! }
107//! ```
108
109// Sometimes we want to use lints which were added after our MSRV.
110// `unknown_lints` is `warn` by default and we deny warnings in CI, so without
111// this attribute, any unknown lint would cause a CI failure when testing with
112// our MSRV.
113#![allow(unknown_lints, non_local_definitions, unreachable_patterns)]
114#![deny(renamed_and_removed_lints)]
115#![deny(
116 anonymous_parameters,
117 deprecated_in_future,
118 late_bound_lifetime_arguments,
119 missing_docs,
120 path_statements,
121 patterns_in_fns_without_body,
122 rust_2018_idioms,
123 trivial_numeric_casts,
124 unreachable_pub,
125 unsafe_op_in_unsafe_fn,
126 unused_extern_crates,
127 // We intentionally choose not to deny `unused_qualifications`. When items
128 // are added to the prelude (e.g., `core::mem::size_of`), this has the
129 // consequence of making some uses trigger this lint on the latest toolchain
130 // (e.g., `mem::size_of`), but fixing it (e.g. by replacing with `size_of`)
131 // does not work on older toolchains.
132 //
133 // We tested a more complicated fix in #1413, but ultimately decided that,
134 // since this lint is just a minor style lint, the complexity isn't worth it
135 // - it's fine to occasionally have unused qualifications slip through,
136 // especially since these do not affect our user-facing API in any way.
137 variant_size_differences
138)]
139#![deny(
140 clippy::all,
141 clippy::alloc_instead_of_core,
142 clippy::arithmetic_side_effects,
143 clippy::as_underscore,
144 clippy::assertions_on_result_states,
145 clippy::as_conversions,
146 clippy::correctness,
147 clippy::dbg_macro,
148 clippy::decimal_literal_representation,
149 clippy::double_must_use,
150 clippy::get_unwrap,
151 clippy::indexing_slicing,
152 clippy::missing_const_for_fn,
153 clippy::missing_inline_in_public_items,
154 clippy::missing_safety_doc,
155 clippy::must_use_candidate,
156 clippy::must_use_unit,
157 clippy::obfuscated_if_else,
158 clippy::perf,
159 clippy::print_stdout,
160 clippy::return_self_not_must_use,
161 clippy::std_instead_of_core,
162 clippy::style,
163 clippy::suspicious,
164 clippy::todo,
165 clippy::undocumented_unsafe_blocks,
166 clippy::unimplemented,
167 clippy::unnested_or_patterns,
168 clippy::unwrap_used,
169 clippy::use_debug
170)]
171#![allow(clippy::type_complexity)]
172#![deny(
173 rustdoc::bare_urls,
174 rustdoc::broken_intra_doc_links,
175 rustdoc::invalid_codeblock_attributes,
176 rustdoc::invalid_html_tags,
177 rustdoc::invalid_rust_codeblocks,
178 rustdoc::missing_crate_level_docs,
179 rustdoc::private_intra_doc_links
180)]
181#![cfg_attr(doc_cfg, feature(doc_cfg))]
182
183use core::marker::PhantomData;
184
185/// A field with safety invariants.
186///
187/// `Unsafe` should not be named directly - instead, use [`unsafe_fields!`] to
188/// declare a type with unsafe fields.
189///
190/// See the [crate-level documentation](crate) for more information.
191#[repr(transparent)]
192pub struct Unsafe<O: ?Sized, F: ?Sized, const NAME_HASH: u128> {
193 _marker: PhantomData<O>,
194 // INVARIANT: `field` is only modified via public `unsafe` methods. User code is never
195 // invoked implicitly except via public `unsafe` methods.
196 field: F,
197}
198
199// NOTE on design: It may seem counter-intuitive to offer an impl of traits that
200// don't require `unsafe` to call. Unfortunately, this is a fundamental
201// requirement if users want to be able to mark their types as `Copy`. Luckily,
202// we can implement `Copy` (and its unavoidable super-trait, `Clone`) without
203// invoking user code or opening up the possibility of modifying the field. We
204// do this by only implementing `Copy` and `Clone` when `F: Copy`. For `Clone`,
205// the user is still able to provide a manual impl, so this does not
206// fundamentally restrict what behavior can be supported.
207impl<O: ?Sized, F: Copy, const NAME_HASH: u128> Copy for Unsafe<O, F, { NAME_HASH }> {}
208impl<O: ?Sized, F: Copy, const NAME_HASH: u128> Clone for Unsafe<O, F, { NAME_HASH }> {
209 #[inline(always)]
210 #[allow(clippy::non_canonical_clone_impl)]
211 fn clone(&self) -> Self {
212 // SAFETY: We don't call any user-defined code here (only make a
213 // bit-for-bit copy of `self.field`), so there's no way to accidentally
214 // invoke user-defined code or modify `self.field`.
215 Unsafe { _marker: PhantomData, field: self.field }
216 }
217}
218
219impl<O: ?Sized, F: ?Sized, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
220 /// Gets a reference to the inner value.
221 ///
222 /// If [`F: Immutable`][immutable], prefer [`as_ref`], which is safe.
223 ///
224 /// [immutable]: zerocopy_0_8::Immutable
225 /// [`as_ref`]: Unsafe::as_ref
226 ///
227 /// # Safety
228 ///
229 /// The caller is responsible for upholding any safety invariants associated
230 /// with this field.
231 #[inline(always)]
232 pub const unsafe fn as_ref_unchecked(&self) -> &F {
233 // SAFETY: This method is unsafe to call.
234 &self.field
235 }
236
237 /// Gets a reference to the inner value safely so long as the inner value is
238 /// immutable.
239 ///
240 /// If [`F: Immutable`][immutable], then `F` does not permit interior
241 /// mutation, and so it is safe to return a reference to it.
242 ///
243 /// [immutable]: zerocopy_0_8::Immutable
244 #[inline(always)]
245 #[cfg(feature = "zerocopy_0_8")]
246 #[cfg_attr(doc_cfg, doc(cfg(feature = "zerocopy_0_8")))]
247 pub const fn as_ref(&self) -> &F
248 where
249 F: zerocopy_0_8::Immutable,
250 {
251 // SAFETY: `F: Immutable` guarantees that the returned `&F` cannot be
252 // used to mutate `self`, and so it cannot be used to violate any
253 // invariant.
254 unsafe { self.as_ref_unchecked() }
255 }
256
257 /// Gets a mutable reference to the inner value.
258 ///
259 /// # Safety
260 ///
261 /// The caller is responsible for upholding any safety invariants associated
262 /// with this field.
263 #[inline(always)]
264 pub unsafe fn as_mut(&mut self) -> &mut F {
265 // SAFETY: This method is unsafe to call.
266 &mut self.field
267 }
268}
269
270impl<O: ?Sized, F, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
271 /// Constructs a new `Unsafe`.
272 ///
273 /// # Safety
274 ///
275 /// The caller is responsible for upholding any safety invariants associated
276 /// with this field.
277 #[inline(always)]
278 pub const unsafe fn new(field: F) -> Unsafe<O, F, { NAME_HASH }> {
279 // SAFETY: This method is unsafe to call.
280 Unsafe { _marker: PhantomData, field }
281 }
282
283 /// Extracts the inner `F` from `self`.
284 #[inline(always)]
285 pub const fn into(self) -> F {
286 use core::mem::ManuallyDrop;
287 let slf = ManuallyDrop::new(self);
288
289 #[repr(C)]
290 union Transmute<Src, Dst> {
291 src: ManuallyDrop<Src>,
292 dst: ManuallyDrop<Dst>,
293 }
294
295 // We'd like to just return `self.field` here, but Rust would drop
296 // `self` in doing that, which we don't want. Even destructuring (ie,
297 // `let Unsafe { field, .. } = self`) also causes a drop. We also can't
298 // use `mem::transmute` because that requires all types to be concrete,
299 // so a union transmute is our only option.
300 //
301 // SAFETY: `ManuallyDrop<Unsafe<_, F, _>>` has the same size and bit
302 // validity as `Unsafe<_, F, _>`. [1] `Unsafe<_, F, _>` is
303 // `#[repr(transparent)]` and has no other fields, and so it has the
304 // same size and bit validity as `F`.
305 //
306 // [1] Per https://doc.rust-lang.org/1.81.0/core/mem/struct.ManuallyDrop.html:
307 //
308 // `ManuallyDrop<T>` is guaranteed to have the same layout and bit
309 // validity as `T`
310 let dst = unsafe { Transmute { src: slf }.dst };
311
312 ManuallyDrop::into_inner(dst)
313 }
314}
315
316/// Defines a type with unsafe fields.
317///
318/// See the [crate-level documentation](crate) for more information.
319// TODO: Allow specifying *which* fields are unsafe.
320#[macro_export]
321macro_rules! unsafe_fields {
322 ($(#[$attr:meta])* $vis:vis struct $name:ident {
323 $($(#[$field_attr:tt])? $field:ident: $field_ty:ty),* $(,)?
324 }) => {
325 $(#[$attr])*
326 $vis struct $name {
327 $(
328 $field: unsafe_fields!(@field $(#[$field_attr])? $field: $field_ty),
329 )*
330 }
331 };
332 (@field #[unsafe] $field:ident: $field_ty:ty) => {
333 $crate::Unsafe<Self, $field_ty, {$crate::macro_util::hash_field_name(stringify!($field))}>
334 };
335 (@field $_field:ident: $field_ty:ty) => {
336 $field_ty
337 }
338}
339
340#[doc(hidden)]
341pub mod macro_util {
342 // TODO: Implement a stronger hash function so we can basically just ignore
343 // collisions. If users encounter collisions in practice, we can just deal
344 // with it then, publish a new version, and tell them to upgrade.
345 #[inline(always)]
346 #[must_use]
347 #[allow(clippy::as_conversions, clippy::indexing_slicing, clippy::arithmetic_side_effects)]
348 pub const fn hash_field_name(field_name: &str) -> u128 {
349 // An implementation of FxHasher, although returning a u128. Probably
350 // not as strong as it could be, but probably more collision resistant
351 // than normal 64-bit FxHasher.
352 let field_name = field_name.as_bytes();
353 let mut hash = 0u128;
354 let mut i = 0;
355 while i < field_name.len() {
356 // This is just FxHasher's `0x517cc1b727220a95` constant
357 // concatenated back-to-back.
358 const K: u128 = 0x517cc1b727220a95517cc1b727220a95;
359 hash = (hash.rotate_left(5) ^ (field_name[i] as u128)).wrapping_mul(K);
360 i += 1;
361 }
362 hash
363 }
364}
365
366#[cfg(test)]
367#[allow(clippy::missing_const_for_fn)]
368mod tests {
369 use super::*;
370
371 unsafe_fields! {
372 /// A `Foo`.
373 #[allow(unused)]
374 struct Foo {
375 #[unsafe]
376 a: usize,
377 b: usize,
378 }
379 }
380
381 unsafe_fields! {
382 /// A `Bar`.
383 #[allow(unused)]
384 struct Bar {
385 #[unsafe]
386 a: usize,
387 #[unsafe]
388 b: usize,
389 }
390 }
391
392 #[test]
393 #[allow(clippy::undocumented_unsafe_blocks)]
394 fn test_unsafe_fieds() {
395 let mut _foo = Foo { a: unsafe { Unsafe::new(0) }, b: 0 };
396 let mut _bar = Bar { a: unsafe { Unsafe::new(0) }, b: unsafe { Unsafe::new(0) } };
397 }
398}
399
400/// This module exists so that we can use rustdoc to perform compile-fail tests
401/// rather than having to set up an entire trybuild set suite.
402///
403/// ```compile_fail,E0308
404/// use unsafe_fields::*;
405///
406/// unsafe_fields! {
407/// struct Foo {
408/// #[unsafe]
409/// a: usize,
410/// b: usize,
411/// }
412/// }
413///
414/// impl Foo {
415/// // Swapping an unsafe field with a non-unsafe field is a compile error.
416/// fn swap(&mut self) {
417/// core::mem::swap(&mut self.a, &mut self.b);
418/// }
419/// }
420/// ```
421///
422/// ```compile_fail,E0308
423/// use unsafe_fields::*;
424///
425/// unsafe_fields! {
426/// struct Foo {
427/// #[unsafe]
428/// a: usize,
429/// #[unsafe]
430/// b: usize,
431/// }
432/// }
433///
434/// impl Foo {
435/// // Swapping an unsafe field with another unsafe field is a compile
436/// // error.
437/// fn swap(&mut self) {
438/// core::mem::swap(&mut self.a, &mut self.b);
439/// }
440/// }
441/// ```
442///
443/// ```compile_fail,E0308
444/// use unsafe_fields::*;
445///
446/// unsafe_fields! {
447/// struct Foo {
448/// #[unsafe]
449/// a: usize,
450/// }
451/// }
452///
453/// unsafe_fields! {
454/// struct Bar {
455/// #[unsafe]
456/// a: usize,
457/// }
458/// }
459///
460/// // Swapping identically-named unsafe fields from different types is a
461/// // compile error.
462/// fn swap(foo: &mut Foo, bar: &mut Bar) {
463/// core::mem::swap(&mut foo.a, &mut bar.a);
464/// }
465/// ```
466#[doc(hidden)]
467pub mod compile_fail {}