From 176b647ccdfb1c89921783e3dd37d1b295fb6c3b Mon Sep 17 00:00:00 2001 From: Gilad Naaman Date: Mon, 4 Nov 2019 12:53:24 +0200 Subject: [PATCH] Added impl for constant offset_of --- .travis.yml | 8 ++++++++ Cargo.toml | 4 ++++ README.md | 52 ++++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 3 +++ src/offset_of.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1c7e80c..2090519 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,14 @@ matrix: script: - sh ci/miri.sh + - env: CONST_OFFSET + rust: nightly + os: linux + script: # `--lib` added to prevent doctests from being compiled. + # This is due to `unstable_const` requiring extra `feature(...)` directives + # which the doctests do not have. + - cargo test --verbose --features unstable_const --lib + - env: RUSTFMT rust: 1.36.0 install: diff --git a/Cargo.toml b/Cargo.toml index dbdfb3d..a7cf2e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,7 @@ rustc_version = "0.2.3" [dev-dependencies] doc-comment = "0.3" + +[features] +default = [] +unstable_const = [] diff --git a/README.md b/README.md index d51cfd5..5d2ad77 100644 --- a/README.md +++ b/README.md @@ -34,20 +34,50 @@ 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, } fn main() { - assert_eq!(offset_of!(Foo, b), 4); - assert_eq!(offset_of!(Foo, d), 4+4+5); + 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), 0..13); - assert_eq!(span_of!(Foo, ..= d), 0..17); - 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); } ``` + +## 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(const_transmute)] +#![feature(const_ptr_offset_from)] +#![feature(ptr_offset_from)] +``` + +and then: + +```rust,ignore +struct Foo { + a: u32, + b: u32, +} + +let foo = [0; offset_of!(Foo, b)] +``` \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 79ff21b..8d7d4f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,9 @@ //! ``` #![no_std] +#![cfg_attr(feature = "unstable_const", feature(const_ptr_offset_from))] +#![cfg_attr(feature = "unstable_const", feature(const_transmute))] +#![cfg_attr(feature = "unstable_const", feature(ptr_offset_from))] #[macro_use] #[cfg(memoffset_doctests)] diff --git a/src/offset_of.rs b/src/offset_of.rs index 0346dce..0308274 100644 --- a/src/offset_of.rs +++ b/src/offset_of.rs @@ -76,6 +76,7 @@ macro_rules! _memoffset__field_check { /// assert_eq!(offset_of!(Foo, b), 4); /// } /// ``` +#[cfg(not(feature = "unstable_const"))] #[macro_export(local_inner_macros)] macro_rules! offset_of { ($parent:path, $field:tt) => {{ @@ -94,6 +95,32 @@ macro_rules! offset_of { }}; } +#[cfg(feature = "unstable_const")] +#[macro_export(local_inner_macros)] +macro_rules! offset_of { + ($parent:path, $field:tt) => {{ + _memoffset__field_check!($parent, $field); + + // Get a base pointer. + // No UB here, and the pointer does not dangle, either. + let uninit = $crate::mem::MaybeUninit::<$parent>::uninit(); + unsafe { + // This, on the other hand, *is* UB, since we're creating a reference + // to uninitialized data. + // Unfortunately it's the best we can do at the moment. + let base_ref = $crate::mem::transmute::<_, &$parent>(&uninit); + let base_u8_ptr = base_ref as *const _ as *const u8; + + // This is another reference to uninitialized data. + // Crucially, we know that this will not trigger a deref coercion because + // of the `field_check!` we did above. + let field_u8_ptr = &base_ref.$field as *const _ as *const u8; + let offset = field_u8_ptr.offset_from(base_u8_ptr) as usize; + offset + } + }}; +} + #[cfg(test)] mod tests { #[test] @@ -145,4 +172,28 @@ mod tests { assert_eq!(offset_of!(sub::Foo, x), 0); } + + #[test] + fn inside_generic_method() { + struct Pair(T, U); + + fn foo(_: Pair) -> usize { + offset_of!(Pair, 1) + } + + assert_eq!(foo(Pair(0, 0)), 4); + } + + #[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); + } }