Bug 1716518 - Upgrade memoffset to v0.5.6. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D117816
This commit is contained in:
Mike Hommey 2021-06-15 22:04:52 +00:00
parent 5b6c28d2da
commit 48d7761513
10 changed files with 321 additions and 99 deletions

6
Cargo.lock generated
View File

@ -3017,11 +3017,11 @@ dependencies = [
[[package]]
name = "memoffset"
version = "0.5.1"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"rustc_version",
"autocfg",
]
[[package]]

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"7a81dbe83677240751ea16029792a46d0a776886ecae85f18fbec6c5912543c3","LICENSE":"3234ac55816264ee7b6c7ee27efd61cf0a1fe775806870e3d9b4c41ea73c5cb1","README.md":"f79665fcf1b936024d74465c45f65d6e3157be593b4507be27729d6eff613047","build.rs":"ba401d756f3007247cdc74f2b749deee11cec48d98d6deaa49677c0cfc3ef0ae","src/lib.rs":"278bbe15039e745385f8bf7cf3b005c4c5404e403c732de5aa936b7dd19cd0e9","src/offset_of.rs":"734576bb3f91e33fe2737d8a036623b5d850521401d6321b844a3da418057dee","src/span_of.rs":"84a073a394254b84f52f3d89aafdd7ce7b87f8587f6a41a5868aa428aa740b19"},"package":"ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"}
{"files":{"Cargo.toml":"3ba713929bae02b40a2cc5c6cc6a0e07281ddb31cc4532129f3845f18e02f8d7","LICENSE":"3234ac55816264ee7b6c7ee27efd61cf0a1fe775806870e3d9b4c41ea73c5cb1","README.md":"a04fb6dddb88b92e8d9393106f078d320269b4e3efe1242f71a9e2984f6f6afd","build.rs":"c9d1b41b0b915d10f84c02476ec13ef30a887cd0c63a2e54269d907981858b28","ci/miri.sh":"ad7410b0a5bd6e346f55e9d96ec0719a085a2d1ce266bddfe6fe73333a1eb8ec","src/lib.rs":"0621a03c51702403e46846f871bab0f576c9557f33b94c37431a57ce81c665d1","src/offset_of.rs":"bdcd14a2645e53b1199839f927cb10aa26dddc935a131fb1b86c7746bc81d162","src/raw_field.rs":"4263b8071a12b2eb81081091a4217fdfbdfa5254f40c94d4af523e84b8b4fd58","src/span_of.rs":"2d9216b794327e95f55bad04ba7d8e76ff5313713c187b319348d8e5620aa0a9"},"package":"043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"}

View File

@ -12,7 +12,7 @@
[package]
name = "memoffset"
version = "0.5.1"
version = "0.5.6"
authors = ["Gilad Naaman <gilad.naaman@gmail.com>"]
description = "offset_of functionality for Rust structs."
readme = "README.md"
@ -20,5 +20,12 @@ keywords = ["mem", "offset", "offset_of", "offsetof"]
categories = ["no-std"]
license = "MIT"
repository = "https://github.com/Gilnaa/memoffset"
[build-dependencies.rustc_version]
version = "0.2.3"
[dev-dependencies.doc-comment]
version = "0.3"
[build-dependencies.autocfg]
version = "1"
[features]
default = []
unstable_const = []
unstable_raw = []

View File

@ -15,40 +15,65 @@ Add the following dependency to your `Cargo.toml`:
```toml
[dependencies]
memoffset = "0.3"
memoffset = "0.5"
```
Versions ">= 0.3" can be used in a constant expression context (though not in a `const fn`),
but require a rust version greater than or equal to 1.33.
These versions will compile fine with rustc versions greater or equal to 1.19, but will
lack support for constant expression.
If you wish to use an older rustc version, lock your dependency to "0.2"
These versions will compile fine with rustc versions greater or equal to 1.19.
Add the following lines at the top of your `main.rs` or `lib.rs` files.
```rust
```rust,ignore
#[macro_use]
extern crate memoffset;
```
## Examples ##
```rust
#[macro_use]
extern crate memoffset;
#[repr(C, packed)]
struct Foo {
a: u32,
b: u32,
c: [u8; 5],
d: u32,
a: u32,
b: u32,
c: [u8; 5],
d: u32,
}
assert_eq!(offset_of!(Foo, b), 4);
assert_eq!(offset_of!(Foo, c[3]), 11);
fn main() {
assert_eq!(offset_of!(Foo, b), 4);
assert_eq!(offset_of!(Foo, d), 4+4+5);
assert_eq!(span_of!(Foo, a), 0..4);
assert_eq!(span_of!(Foo, a .. c), 0..8);
assert_eq!(span_of!(Foo, a .. c[1]), 0..9);
assert_eq!(span_of!(Foo, a ..= c[1]), 0..10);
assert_eq!(span_of!(Foo, ..= d), 0..14);
assert_eq!(span_of!(Foo, b ..), 4..17);
assert_eq!(span_of!(Foo, a), 0..4);
assert_eq!(span_of!(Foo, a .. c), 0..8);
assert_eq!(span_of!(Foo, a ..= c), 0..13);
assert_eq!(span_of!(Foo, ..= d), 0..17);
assert_eq!(span_of!(Foo, b ..), 4..17);
}
```
## Feature flags ##
### Usage in constants ###
`memoffset` has **experimental** support for compile-time `offset_of!` on a nightly compiler.
In order to use it, you must enable the `unstable_const` crate feature and several compiler features.
Cargo.toml:
```toml
[dependencies.memoffset]
version = "0.5"
features = ["unstable_const"]
```
Your crate root: (`lib.rs`/`main.rs`)
```rust,ignore
#![feature(ptr_offset_from, const_ptr_offset_from, const_maybe_uninit_as_ptr, const_raw_ptr_deref)]
```
If you intend to use `offset_of!` inside a `const fn`, also add the `const_fn` compiler feature.
### Raw references ###
Recent nightlies support [a way to create raw pointers](https://github.com/rust-lang/rust/issues/73394) that avoids creating intermediate safe references.
`memoffset` can make use of that feature to avoid what is technically Undefined Behavior.
Use the `unstable_raw` feature to enable this.

View File

@ -1,12 +1,16 @@
extern crate rustc_version;
use rustc_version::{version, Version};
extern crate autocfg;
fn main() {
// Assert we haven't travelled back in time
assert!(version().unwrap().major >= 1);
let ac = autocfg::new();
// Check for a minimum version
if version().unwrap() >= Version::parse("1.36.0").unwrap() {
println!("cargo:rustc-cfg=memoffset_maybe_uninit");
// Check for a minimum version for a few features
if ac.probe_rustc_version(1, 31) {
println!("cargo:rustc-cfg=allow_clippy");
}
if ac.probe_rustc_version(1, 36) {
println!("cargo:rustc-cfg=maybe_uninit");
}
if ac.probe_rustc_version(1, 40) {
println!("cargo:rustc-cfg=doctests");
}
}

14
third_party/rust/memoffset/ci/miri.sh vendored Normal file
View File

@ -0,0 +1,14 @@
set -ex
# Install Miri.
MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)
echo "Installing latest nightly with Miri: $MIRI_NIGHTLY"
rustup default "$MIRI_NIGHTLY"
rustup component add miri
# Run tests.
cargo miri test
cargo miri test --all-features
# Restore old state in case Travis uses this cache for other jobs.
rustup default nightly

View File

@ -20,11 +20,7 @@
//! A crate used for calculating offsets of struct members and their spans.
//!
//! Some of the funcationality of the crate makes no sense when used along with structs that
//! are not `#[repr(C, packed)]`, but it is up to the user to make sure that they are.
//!
//! This functionality should work for `const`s but presently doesn't work on `const fn`. Storing a
//! value in a const and then returning it from a `const fn` should workaround most cases.
//! This functionality currently can not be used in compile time code such as `const` or `const fn` definitions.
//!
//! ## Examples
//! ```
@ -61,15 +57,35 @@
//! ```
#![no_std]
#![cfg_attr(
feature = "unstable_const",
feature(
ptr_offset_from,
const_fn,
const_ptr_offset_from,
const_maybe_uninit_as_ptr,
const_raw_ptr_deref,
)
)]
#![cfg_attr(feature = "unstable_raw", feature(raw_ref_macros))]
#[macro_use]
#[cfg(doctests)]
#[cfg(doctest)]
extern crate doc_comment;
#[cfg(doctests)]
#[cfg(doctest)]
doctest!("../README.md");
// This `use` statement enables the macros to use `$crate::mem`.
// Doing this enables this crate to function under both std and no-std crates.
#[doc(hidden)]
pub use core::mem;
#[doc(hidden)]
pub use core::ptr;
#[macro_use]
mod raw_field;
#[macro_use]
mod offset_of;
#[macro_use]

View File

@ -20,41 +20,49 @@
/// Macro to create a local `base_ptr` raw pointer of the given type, avoiding UB as
/// much as is possible currently.
#[cfg(memoffset_maybe_uninit)]
#[cfg(maybe_uninit)]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__let_base_ptr {
($name:ident, $type:tt) => {
($name:ident, $type:path) => {
// No UB here, and the pointer does not dangle, either.
// But we have to make sure that `uninit` lives long enough,
// so it has to be in the same scope as `$name`. That's why
// `let_base_ptr` declares a variable (several, actually)
// instad of returning one.
// instead of returning one.
let uninit = $crate::mem::MaybeUninit::<$type>::uninit();
let $name = uninit.as_ptr();
let $name: *const $type = uninit.as_ptr();
};
}
#[cfg(not(memoffset_maybe_uninit))]
#[cfg(not(maybe_uninit))]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__let_base_ptr {
($name:ident, $type:tt) => {
// No UB right here, but we will later offset into a field
// of this pointer, and that is UB when the pointer is dangling.
let non_null = $crate::ptr::NonNull::<$type>::dangling();
let $name = non_null.as_ptr() as *const $type;
($name:ident, $type:path) => {
// No UB right here, but we will later dereference this pointer to
// offset into a field, and that is UB because the pointer is dangling.
let $name = $crate::mem::align_of::<$type>() as *const $type;
};
}
/// Deref-coercion protection macro.
/// Macro to compute the distance between two pointers.
#[cfg(feature = "unstable_const")]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__field_check {
($type:tt, $field:tt) => {
// Make sure the field actually exists. This line ensures that a
// compile-time error is generated if $field is accessed through a
// Deref impl.
let $type { $field: _, .. };
macro_rules! _memoffset_offset_from {
($field:expr, $base:expr) => {
// Compute offset, with unstable `offset_from` for const-compatibility.
// (Requires the pointers to not dangle, but we already need that for `raw_field!` anyway.)
unsafe { ($field as *const u8).offset_from($base as *const u8) as usize }
};
}
#[cfg(not(feature = "unstable_const"))]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset_offset_from {
($field:expr, $base:expr) => {
// Compute offset.
($field as usize) - ($base as usize)
};
}
@ -79,17 +87,13 @@ macro_rules! _memoffset__field_check {
/// ```
#[macro_export(local_inner_macros)]
macro_rules! offset_of {
($parent:tt, $field:tt) => {{
_memoffset__field_check!($parent, $field);
// Get a base pointer.
($parent:path, $field:tt) => {{
// Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
_memoffset__let_base_ptr!(base_ptr, $parent);
// Get the field address. This is UB because we are creating a reference to
// the uninitialized field.
#[allow(unused_unsafe)] // for when the macro is used in an unsafe block
let field_ptr = unsafe { &(*base_ptr).$field as *const _ };
let offset = (field_ptr as usize) - (base_ptr as usize);
offset
// Get field pointer.
let field_ptr = raw_field!(base_ptr, $parent, $field);
// Compute offset.
_memoffset_offset_from!(field_ptr, base_ptr)
}};
}
@ -110,7 +114,7 @@ mod tests {
}
#[test]
#[cfg(not(miri))] // this creates unaligned references
#[cfg_attr(miri, ignore)] // this creates unaligned references
fn offset_simple_packed() {
#[repr(C, packed)]
struct Foo {
@ -132,4 +136,77 @@ mod tests {
assert_eq!(offset_of!(Tup, 0), 0);
assert_eq!(offset_of!(Tup, 1), 4);
}
#[test]
fn path() {
mod sub {
#[repr(C)]
pub struct Foo {
pub x: u32,
}
}
assert_eq!(offset_of!(sub::Foo, x), 0);
}
#[test]
fn inside_generic_method() {
struct Pair<T, U>(T, U);
fn foo<T, U>(_: Pair<T, U>) -> usize {
offset_of!(Pair<T, U>, 1)
}
assert_eq!(foo(Pair(0, 0)), 4);
}
#[test]
fn test_raw_field() {
#[repr(C)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
let f: Foo = Foo {
a: 0,
b: [0, 0],
c: 0,
};
let f_ptr = &f as *const _;
assert_eq!(f_ptr as usize + 0, raw_field!(f_ptr, Foo, a) as usize);
assert_eq!(f_ptr as usize + 4, raw_field!(f_ptr, Foo, b) as usize);
assert_eq!(f_ptr as usize + 8, raw_field!(f_ptr, Foo, c) as usize);
}
#[cfg(feature = "unstable_const")]
#[test]
fn const_offset() {
#[repr(C)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
assert_eq!([0; offset_of!(Foo, b)].len(), 4);
}
#[cfg(feature = "unstable_const")]
#[test]
fn const_fn_offset() {
const fn test_fn() -> usize {
#[repr(C)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
offset_of!(Foo, b)
}
assert_eq!([0; test_fn()].len(), 4);
}
}

View File

@ -0,0 +1,84 @@
// Copyright (c) 2020 Gilad Naaman, Ralf Jung
//
// 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.
/// `raw_const!`, or just ref-then-cast when that is not available.
#[cfg(feature = "unstable_raw")]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__raw_const {
($path:expr) => {{
$crate::ptr::raw_const!($path)
}};
}
#[cfg(not(feature = "unstable_raw"))]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__raw_const {
($path:expr) => {{
// This is UB because we create an intermediate reference to uninitialized memory.
// Nothing we can do about that without `raw_const!` though.
&$path as *const _
}};
}
/// Deref-coercion protection macro.
#[cfg(allow_clippy)]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__field_check {
($type:path, $field:tt) => {
// Make sure the field actually exists. This line ensures that a
// compile-time error is generated if $field is accessed through a
// Deref impl.
#[allow(clippy::unneeded_field_pattern)]
let $type { $field: _, .. };
};
}
#[cfg(not(allow_clippy))]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__field_check {
($type:path, $field:tt) => {
// Make sure the field actually exists. This line ensures that a
// compile-time error is generated if $field is accessed through a
// Deref impl.
let $type { $field: _, .. };
};
}
/// Computes a const raw pointer to the given field of the given base pointer
/// to the given parent type.
///
/// The `base` pointer *must not* be dangling, but it *may* point to
/// uninitialized memory.
#[macro_export(local_inner_macros)]
macro_rules! raw_field {
($base:expr, $parent:path, $field:tt) => {{
_memoffset__field_check!($parent, $field);
// Get the field address.
// Crucially, we know that this will not trigger a deref coercion because
// of the field check we did above.
#[allow(unused_unsafe)] // for when the macro is used in an unsafe block
unsafe {
_memoffset__raw_const!((*($base as *const $parent)).$field)
}
}};
}

View File

@ -93,61 +93,56 @@ macro_rules! span_of {
(@helper $root:ident, [] ..) => {
_memoffset__compile_error!("Expected a range, found '..'")
};
// Lots of UB due to taking references to uninitialized fields! But that can currently
// not be avoided.
// No explicit begin for range.
(@helper $root:ident, $parent:tt, [] ..) => {{
(@helper $root:ident, $parent:path, [] ..) => {{
// UB due to taking references to uninitialized fields for `size_of_val`.
($root as usize,
$root as usize + $crate::mem::size_of_val(&(*$root)))
}};
(@helper $root:ident, $parent:tt, [] ..= $field:tt) => {{
_memoffset__field_check!($parent, $field);
(@helper $root:ident, $parent:path, [] ..= $field:tt) => {{
// UB due to taking references to uninitialized fields for `size_of_val`.
($root as usize,
&(*$root).$field as *const _ as usize + $crate::mem::size_of_val(&(*$root).$field))
raw_field!($root, $parent, $field) as usize + $crate::mem::size_of_val(&(*$root).$field))
}};
(@helper $root:ident, $parent:tt, [] .. $field:tt) => {{
_memoffset__field_check!($parent, $field);
($root as usize, &(*$root).$field as *const _ as usize)
(@helper $root:ident, $parent:path, [] .. $field:tt) => {{
($root as usize, raw_field!($root, $parent, $field) as usize)
}};
// Explicit begin and end for range.
(@helper $root:ident, $parent:tt, # $begin:tt [] ..= $end:tt) => {{
_memoffset__field_check!($parent, $begin);
_memoffset__field_check!($parent, $end);
(&(*$root).$begin as *const _ as usize,
&(*$root).$end as *const _ as usize + $crate::mem::size_of_val(&(*$root).$end))
(@helper $root:ident, $parent:path, # $begin:tt [] ..= $end:tt) => {{
// UB due to taking references to uninitialized fields for `size_of_val`.
(raw_field!($root, $parent, $begin) as usize,
raw_field!($root, $parent, $end) as usize + $crate::mem::size_of_val(&(*$root).$end))
}};
(@helper $root:ident, $parent:tt, # $begin:tt [] .. $end:tt) => {{
_memoffset__field_check!($parent, $begin);
_memoffset__field_check!($parent, $end);
(&(*$root).$begin as *const _ as usize,
&(*$root).$end as *const _ as usize)
(@helper $root:ident, $parent:path, # $begin:tt [] .. $end:tt) => {{
(raw_field!($root, $parent, $begin) as usize,
raw_field!($root, $parent, $end) as usize)
}};
// No explicit end for range.
(@helper $root:ident, $parent:tt, # $begin:tt [] ..) => {{
_memoffset__field_check!($parent, $begin);
(&(*$root).$begin as *const _ as usize,
(@helper $root:ident, $parent:path, # $begin:tt [] ..) => {{
// UB due to taking references to uninitialized fields for `size_of_val`.
(raw_field!($root, $parent, $begin) as usize,
$root as usize + $crate::mem::size_of_val(&*$root))
}};
(@helper $root:ident, $parent:tt, # $begin:tt [] ..=) => {{
(@helper $root:ident, $parent:path, # $begin:tt [] ..=) => {{
_memoffset__compile_error!(
"Found inclusive range to the end of a struct. Did you mean '..' instead of '..='?")
}};
// Just one field.
(@helper $root:ident, $parent:tt, # $begin:tt []) => {{
_memoffset__field_check!($parent, $begin);
(&(*$root).$begin as *const _ as usize,
&(*$root).$begin as *const _ as usize + $crate::mem::size_of_val(&(*$root).$begin))
(@helper $root:ident, $parent:path, # $begin:tt []) => {{
// UB due to taking references to uninitialized fields for `size_of_val`.
(raw_field!($root, $parent, $begin) as usize,
raw_field!($root, $parent, $begin) as usize + $crate::mem::size_of_val(&(*$root).$begin))
}};
// Parsing.
(@helper $root:ident, $parent:tt, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{
(@helper $root:ident, $parent:path, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{
span_of!(@helper $root, $parent, $(#$begin)* #$tt [] $($rest)*)
}};
(@helper $root:ident, $parent:tt, [] $tt:tt $($rest:tt)*) => {{
(@helper $root:ident, $parent:path, [] $tt:tt $($rest:tt)*) => {{
span_of!(@helper $root, $parent, #$tt [] $($rest)*)
}};
// Entry point.
($sty:tt, $($exp:tt)+) => ({
($sty:path, $($exp:tt)+) => ({
unsafe {
// Get a base pointer.
_memoffset__let_base_ptr!(root, $sty);
@ -177,7 +172,7 @@ mod tests {
}
#[test]
#[cfg(not(miri))] // this creates unaligned references
#[cfg_attr(miri, ignore)] // this creates unaligned references
fn span_simple_packed() {
#[repr(C, packed)]
struct Foo {